14

I'd like to define a new command with optional parameters using the xparse package. Please consider the following example:

\documentclass{minimal}
\usepackage{xparse}

\DeclareDocumentCommand{\mycommand}{ O{mydefault} m o o o }{%
        p:#2%
        \IfNoValueTF{#3}%
            {}%
            { p:#3}%
        \IfNoValueTF{#4}%
            {}%
            { p:#4}%
        \IfNoValueTF{#5}%
            {}%
            { p:#5}
        p:#1
}
\begin{document}
\mycommand[one]{two} \par % p:two p:one
\mycommand[one]{two}[three] \par % p:two p:three p:one
\mycommand[one]{two}[three][four] \par % p:two p:three p:four p:one
\mycommand[one]{two}[three][four][five] \par % p:two p:three p:four p:five p:one
\mycommand[one]{two}[three][][five] \par % p:two p:three p: p:five p:one
\mycommand[one]{two}[][][five] % p:two p: p: p:five p:one
\end{document}

enter image description here

The problem now is that when I'm leaving some parameters empty between two others that are filled in, LaTeX also displays those empty parameters. Applied to the last example I'd like to get p:two p:five p:one.

4
  • 3
    @Werner has provided an explanation of the fact that [] is different from an optional argument which is missing. However, I'd point out that runs of optional arguments don't really make for good interface design in most cases. What is the broader context here?
    – Joseph Wright
    Commented Nov 5, 2011 at 17:23
  • 1
    I agree, that one optional argument should be enough. If you have more optional arguments, you should use a key=value interface. Even a lot of mandatory arguments are difficult to learn. Nobody wants to learn which of several arguments are optional and which are mandatory and how many optional arguments have to be empty to set the one you want to use. Commented Nov 5, 2011 at 17:43
  • 1
    @JosephWright I agree with you that this isn't a good interface design but I don't see a better way to achieve the desired result. I'm using the command to refer to acts (e.g. Art. 1 Abs. 2 Ziff. 3 Bst. a XYZ, Art. 1 Bst. a XYZ, ...). And as I'm a lazy guy, it's easier just to write \art{1}[2][3][a]. But if you know a better solution how to handle this please tell it to me.
    – Thorsten
    Commented Nov 5, 2011 at 17:43
  • @Schweinebacke What would a key=value interface look like?
    – Andyc
    Commented Feb 17, 2022 at 17:01

2 Answers 2

13

You have to make an additional comparison on those arguments specified as [], which technically differ from \NoValue. And, you can't just leave them out, since subsequent optional arguments would then be used out-of-sequence. You can use the ifmtarg package:

enter image description here

\documentclass{minimal}
\usepackage{xparse}% http://ctan.org/pkg/xparse
\usepackage{ifmtarg}% http://ctan.org/pkg/ifmtarg
\makeatletter
\DeclareDocumentCommand{\mycommand}{ O{mydefault} m o o o }{%
        p:#2%
        \IfNoValueTF{#3}%
            {}%
            {\@ifmtarg{#3}{}{ p:#3}}%
        \IfNoValueTF{#4}%
            {}%
            {\@ifmtarg{#4}{}{ p:#4}}%
        \IfNoValueTF{#5}%
            {}%
            {\@ifmtarg{#5}{}{ p:#5}}
        p:#1
}
\makeatother
\begin{document}
\mycommand[one]{two} \par % p:two p:one
\mycommand[one]{two}[three] \par % p:two p:three p:one
\mycommand[one]{two}[three][four] \par % p:two p:three p:four p:one
\mycommand[one]{two}[three][four][five] \par % p:two p:three p:four p:five p:one
\mycommand[one]{two}[three][][five] \par % p:two p:three p:five p:one
\mycommand[one]{two}[][][five] % p:two p:five p:one
\end{document}
12

Without other packages, you can define a test yourself

\makeatletter
\def\IfEmptyTF#1{%
  \if\relax\detokenize{#1}\relax
    \expandafter\@firstoftwo
  \else
    \expandafter\@secondoftwo
  \fi}
\makeatother
\DeclareDocumentCommand{\mycommand}{ O{mydefault} m O{} O{} O{} }{%
        p:#2%
    \IfEmptyTF{#3}%
            {}%
            { p:#3}%
        \IfEmptyTF{#4}%
            {}%
            { p:#4}%
        \IfEmptyTF{#5}%
            {}%
            { p:#5}
        p:#1
}

The problem is that \NoValue is used if the optional argument is not specified; but [] means "the optional argument is empty".

The test for emptyness is already provided by LaTeX3 packages, so its definition can be changed into

\ExplSyntaxOn
\cs_set_eq:NN \IfEmptyTF \tl_if_blank:nTF
\ExplSyntaxOff
3
  • I'm not sure why both you and @Werner feel the need to define (yet again) empty tests. What's wrong with expl3's \tl_if_blank:nTF? (Your approach also gives a non-expandable test as it can insert a \relax, which if you are using LaTeX3 should therefore be protected.)
    – Joseph Wright
    Commented Nov 5, 2011 at 17:19
  • 3
    @JosephWright Nothing wrong, but it's not at the same level as \IfNoValueTF, isn't it? And it's not documented in xparse. I believe that xparse should provide a test for emptyness and, maybe, a string equality test. My test for emptyness never leaves a \relax (it's Heiko Oberdiek's, to be honest).
    – egreg
    Commented Nov 5, 2011 at 17:22
  • 2
    @JosephWright Moreover, \tl_if_blank:nTF can't be used without saying \ExplSyntaxOn, which isn't even mentioned in the documentation of xparse.
    – egreg
    Commented Nov 5, 2011 at 17:37

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .