11

I am trying to define a command that takes two arguments, where one is optional. If the optional argument isn't given I would like the compulsory argument to be used for the optional argument.

For example:

\necommand{\foo}[2][#2]{#1 foo(#2)}

which would return:

IN: \foo[hello]{hi}      OUT: hello foo(hi)
IN: \foo{hi}             OUT: hi foo(hi)

Ideally it would be nice to keep it simple and use standard LaTeX with no packages. Thanks in advance!

  • 1
    Your \foo{hello}{hi} would produce hello foo(hello) since it is called the wrong way! \foo[hello]{hi} is correct. Please not that you can't use #2 in the argument description at all, so your command is not correct – user31729 Jan 31 '17 at 16:23
  • 1
    I disagree that the question is the duplicate of the one that was mentioned. – egreg Jan 31 '17 at 20:34
14

The classical approach for this is to use \@dblarg:

\documentclass{article}

\makeatletter
\newcommand{\foo}{\@dblarg\ah@foo}
\def\ah@foo[#1]#2{#1 foo(#2)}
\makeatother

\begin{document}

No optional argument: \foo{xyz}

Optional argument: \foo[abc]{xyz}

\end{document}

enter image description here

With xparse it's easier:

\documentclass{article}
\usepackage{xparse}

\NewDocumentCommand{\foo}{om}{%
  \IfNoValueTF{#1}{#2}{#1} foo(#2)%
}

\begin{document}

No optional argument: \foo{xyz}

Optional argument: \foo[abc]{xyz}

\end{document}

Wherever you need the optional argument, you type \IfNoValueTF{#1}{#2}{#1}.

There's a much slicker way if you have xparse released 2017/02/10 (or later): an optional argument O can take as default any of the mandatory argument:

\documentclass{article}
\usepackage{xparse}

\NewDocumentCommand{\foo}{ O{#2} m }{%
  #1 foo(#2)%
}

\begin{document}

No optional argument: \foo{xyz}

Optional argument: \foo[abc]{xyz}

\end{document}

So we're telling \foo that, if the optional argument is missing (first call), the mandatory argument #2 should be used also as value for #1. In the second call, the optional argument is given, so it's substituted for #1.

  • With the latest version of xparse (not yet on CTAN), it's now possible for default values of arguments to refer to any other argument. – Bruno Le Floch Feb 12 '17 at 6:26
  • @BrunoLeFloch I'm waiting for it to be in wide distribution for updating my answer. – egreg Feb 12 '17 at 17:15
10

This is pretty easy with \NewDocumentCommand from xparse, checking whether the optional argument (o) was given or not with \IfValueTF{#1}{}{}.

Not so easy with \newcommand, however. Define a command without arguments, say \foobar and check with \@ifnextchar[{}{} whether the next character is a [ and branch into a command that uses []{} arguments and another one, that does only use the mandatory argument, i.e. {}. This technique is called 'moving arguments'. This way does not need other extra packages and applies LaTeX core features. The only 'tricky' point is to use the \makeatletter...\makeatother pair.

\@ifnextchar[ looks for [ and if this is found, the character is basically 'shifted' back such that \foobar@opt can find it again as start of a command with optional argument (Actually, [ is stored to a temporary macro and expanded for the true branch if [ has been found)

\documentclass{article}

\usepackage{xparse}

\makeatletter
\newcommand{\foobar}{%
  \@ifnextchar[{\foobar@opt}{\foobar@noopt}
}{}

\newcommand{\foobar@opt}[2][]{%
  #1 foo(#2)%
}

\newcommand{\foobar@noopt}[1]{%
  #1 foo(#1)%
}
\makeatother

\NewDocumentCommand{\foo}{om}{%
  \IfValueTF{#1}{%
    #1 foo(#2)%
  }{%
    #2 foo(#2)%
  }%
}


\begin{document}
\foo[hello]{hi}

\foo{hi}       


\foobar[hello]{hi}

\foobar{hi}       

\end{document}

enter image description here

  • Thanks for the quick answer, just one question. Would this work in a maths environment? – Aldahunter Jan 31 '17 at 16:28
  • 1
    @AldaHunter: Yes, of course, apart from using another font for 'foo' (or whatever you want to type there) – user31729 Jan 31 '17 at 16:35
  • Why not simply \newcommand\foo[2][]{\if\relax\dezokenize{#1}\relax#2 \else#1 \fi foo(#2)}? – clemens Jan 31 '17 at 16:38
  • Oh, just seen: Werner uses the same idea... :) – clemens Jan 31 '17 at 16:40
  • Well, there's \@dblarg – egreg Jan 31 '17 at 16:40
6

You can supply a default value for the optional argument, and use this to condition on whether or not it was supplied in the first place.

enter image description here

\documentclass{article}

\newcommand{\foo}[2][\relax]{%
  \ifx\relax#1\relax #2\else #1\fi
  ~foo(#2)%
}

\begin{document}

\foo[hello]{hi}

\foo[]{hi}

\foo{hi}       

\end{document}

Above I specified that the optional argument will default to \relax if it is not supplied, and then check \ifx\relax#1\relax to either place #2 or #1. \ifx compares the following two tokens for equivalence.

  • 3
    What if you call \foo[\relax x]{y}? ;-) – egreg Jan 31 '17 at 16:45
  • 1
    @egreg: Yes; only you would do that. :) – Werner Jan 31 '17 at 16:46
  • 3
    \foo[\relax x]{egreg is not alone} – David Carlisle Jan 31 '17 at 17:16
  • 4
    @egreg: more realistic use: \foo[\protect\emph{x}]{y} – Bruno Le Floch Jan 31 '17 at 19:22
2

I favor the answer of egreg, i.e., the suggestion of using \@dblarg.

Werner suggested checking for a "default value" in terms of an \ifx-comparison.

Checking for a default value is somewhat different from checking whether an optional argument was provided at all as the case of the optional argument not being provided at all is not distinguished from the case of the optional argument being provided explicitly with the default value.

\ifx-comparison implies that

  • either comparison is to be done in a non expandable way by defining and \ifx-comparing temporary macros
  • or \ifx-comparison is to be applied directly to the token forming the default value and the token(s) actually supplied as optional argument.

In the latter case \ifx-comparison imposes some restrictions on what can be used as default value and is not completely "waterproof":

  • Default values cannot consist of several tokens.
  • As \ifx-comparison does compare single tokens, not macro arguments, \ifx-comparison can be outmanoeuvred in several erroneous ways in edge cases where the arguments consist of more than one token.
  • An \ifx-comparison can also be outmanoeuvred by the edge case of arguments containing unbalanced \else or \fi.
  • \ifx-comparisons can be outmanoeuvred by using control sequence tokens or active character tokens \let equal to the default-value-token.

If going the route of checking for a default value in an expandable way, i.e., not defining some temporary macros and \ifx-comparing them, I suggest

  • either having "emptiness" as default value and checking for emptiness:

    \documentclass{article}
    \makeatletter
    %%=========================================================================
    %% Paraphernalia:
    %%    \UD@firstoftwo, \UD@secondoftwo
    %%.........................................................................
    \newcommand\UD@firstoftwo[2]{#1}%
    \newcommand\UD@secondoftwo[2]{#2}%
    %%-------------------------------------------------------------------------
    %% Check whether argument is empty:
    %%.........................................................................
    %% \UD@CheckWhetherNull{<Argument which is to be checked>}%
    %%                     {<Tokens to be delivered in case that argument
    %%                       which is to be checked is empty>}%
    %%                     {<Tokens to be delivered in case that argument
    %%                       which is to be checked is not empty>}%
    %%
    %% The gist of this macro comes from Robert R. Schneck's \ifempty-macro:
    %% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
    %%
    %% A concern in his posting is that the argument is hit with \string
    %% after some expansions which in edge cases might result in unbalancing
    %% surrounding \if..\fi-constructs if the macro is used inside of such
    %% \if..\fi-constructs.
    %%
    %% That challenging concern sickened me. ;-)
    %%
    %% Therefore I decided to implerment a variant where this cannot happen
    %% as expansion is forced by \romannumeral:
    %%
    %% After the first expansion-step, \string is not applied yet.
    %% After the second expansion-step, any possibly disturbing remainders
    %% are already removed due to \romannumeral-expansion.
    %%
    %% No eTeX- or whatsoever extensions. No \if.. .Only \romannumeral,
    %% digit 0, space token for terminating \romannumeral-expansion,
    %% \string, \expandafter, \UD@firstoftwo, \UD@secondoftwo, {, }.
    %%
    %% May 20, 2016
    %%
    %% Ulrich Diez (e-mail: ud.usenetcorrespondence@web.de)
    %%
    \newcommand\UD@CheckWhetherNull[1]{%
      \romannumeral0\expandafter\UD@secondoftwo\string{\expandafter
      \UD@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
      \UD@secondoftwo\string}\expandafter\UD@firstoftwo\expandafter{\expandafter
      \UD@secondoftwo\string}\expandafter\expandafter\UD@firstoftwo{ }{}%
      \UD@secondoftwo}{\expandafter\expandafter\UD@firstoftwo{ }{}\UD@firstoftwo}%
    }%
    
    \newcommand{\foo}[2][]{%
      \UD@CheckWhetherNull{#1}{#2}{#1}~foo(#2)%
    }%
    
    \makeatother
    
    \parindent=0ex
    \parskip=\bigskipamount
    
    \begin{document}
    
    Empty optional argument  or no optional argument will be given---%
    \verb|\foo{hi}|:\\
    \foo{hi}
    
    Empty optional argument  or no optional argument will be given---%
    \verb|\foo[]{hi}|:\\
    \foo[]{hi}
    
    Empty optional argument  or no optional argument will be given---%
    \verb|\foo[{}]{hi}|:\\
    \foo[{}]{hi}
    
    A nice optional argument will be given---%
    \verb|\foo[hello]{hi}|:\\
    \foo[hello]{hi}
    
    \end{document}
    

    enter image description here

  • or checking for the default value by means of macros that process delimited arguments:

    \documentclass{article}
    \makeatletter
    %%=========================================================================
    %% Paraphernalia:
    %%    \UD@firstoftwo, \UD@secondoftwo
    %%.........................................................................
    \newcommand\UD@firstoftwo[2]{#1}%
    \newcommand\UD@secondoftwo[2]{#2}%
    %%-------------------------------------------------------------------------
    %% Check whether argument is empty:
    %%.........................................................................
    %% \UD@CheckWhetherNull{<Argument which is to be checked>}%
    %%                     {<Tokens to be delivered in case that argument
    %%                       which is to be checked is empty>}%
    %%                     {<Tokens to be delivered in case that argument
    %%                       which is to be checked is not empty>}%
    %%
    %% The gist of this macro comes from Robert R. Schneck's \ifempty-macro:
    %% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
    %%
    %% A concern in his posting is that the argument is hit with \string
    %% after some expansions which in edge cases might result in unbalancing
    %% surrounding \if..\fi-constructs if the macro is used inside of such
    %% \if..\fi-constructs.
    %%
    %% That challenging concern sickened me. ;-)
    %%
    %% Therefore I decided to implerment a variant where this cannot happen
    %% as expansion is forced by \romannumeral:
    %%
    %% After the first expansion-step, \string is not applied yet.
    %% After the second expansion-step, any possibly disturbing remainders
    %% are already removed due to \romannumeral-expansion.
    %%
    %% No eTeX- or whatsoever extensions. No \if.. .Only \romannumeral,
    %% digit 0, space token for terminating \romannumeral-expansion,
    %% \string, \expandafter, \UD@firstoftwo, \UD@secondoftwo, {, }.
    %%
    %% May 20, 2016
    %%
    %% Ulrich Diez (e-mail: ud.usenetcorrespondence@web.de)
    %%
    \newcommand\UD@CheckWhetherNull[1]{%
      \romannumeral0\expandafter\UD@secondoftwo\string{\expandafter
      \UD@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
      \UD@secondoftwo\string}\expandafter\UD@firstoftwo\expandafter{\expandafter
      \UD@secondoftwo\string}\expandafter\expandafter\UD@firstoftwo{ }{}%
      \UD@secondoftwo}{\expandafter\expandafter\UD@firstoftwo{ }{}\UD@firstoftwo}%
    }%
    %%-------------------------------------------------------------------------
    %% Check whether argument contains no exclamation-mark on top-brace-level:
    %%.........................................................................
    %% \UD@CheckWhetherNoExclamationMark{<Argument which is to be checked>}%
    %%                  {<Tokens to be delivered in case that
    %%                    argument which is to be checked does not contain !>}%
    %%                  {<Tokens to be delivered in case that
    %%                    argument which is to be checked does contain !>}%
    \long\def\UD@RemoveToExclamationMark#1!{}%
    \long\def\UD@CheckWhetherNoExclamationMark#1{%
      \expandafter\UD@CheckWhetherNull\expandafter{\UD@RemoveToExclamationMark#1!}%
    }%
    %%-------------------------------------------------------------------------
    %% Fork depending on some tokens:
    %%.........................................................................
    %%\\CheckWhetherDefault{<Argument which is to be checked>}%
    %%           {<Tokens to be delivered in case argument is "Default value">}%
    %%           {<Tokens to be delivered in case argument is not "Default value">}%
    %%
    %% In case <Argument which is to be checked> is neither "case 1" nor
    %% "case 2" the phrase "Error: Unknown parameter ``<Argument which is
    %% to be checked>'' to \CheckWhetherDefault." will be delivered.
    %%
    \newcommand\@CheckWhetherDefault{}%
    \long\def\@CheckWhetherDefault%
       #1!!Default value!#2#3!!!!{#2}%
    \newcommand\CheckWhetherDefault[1]{%
      \romannumeral0%
        \UD@CheckWhetherNoExclamationMark{#1}{%
          \@CheckWhetherDefault
          !#1!Default value!{\UD@firstoftwo}% <- #1 is empty.
          !!#1!{\UD@firstoftwo}% <- #1 = Default value
          !!Default value!{\UD@secondoftwo}% <- #1 = something else without exclamation mark
          !!!!%
        }{\UD@secondoftwo}% <- #1 = something else with exclamation mark
    }%
    \makeatother
    
    \newcommand{\foo}[2][Default value]{%
      \CheckWhetherDefault{#1}{#2}%
                              {#1}%
      ~foo(#2)%
    }%
    
    \parindent=0ex
    \parskip=\bigskipamount
    
    \begin{document}
    
    ``Default value'' or empty optional argument  or no optional argument will be given---%
    \verb|\foo{hi}|:\\
    \foo{hi}
    
    ``Default value'' or empty optional argument  or no optional argument will be given---%
    \verb|\foo[Default value]{hi}|:\\
    \foo[Default value]{hi}
    
    ``Default value'' or empty optional argument  or no optional argument will be given---%
    \verb|\foo[]{hi}|:\\
    \foo[]{hi}
    
    ``Default value'' or empty optional argument  or no optional argument will be given---%
    \verb|\foo[{}]{hi}|:\\
    \foo[{}]{hi}
    
    A nice optional argument will be given---%
    \verb|\foo[hello]{hi}|:\\
    \foo[hello]{hi}
    
    \end{document}
    

    enter image description here

You can also go the boring non expandable route of defining and \ifx-comparing temporary macros:

\documentclass{article}

\makeatletter
\newcommand{\foo}[2][Default value]{%
  \begingroup
  \def\mytempa{#1}%
  \def\mytempb{Default value}%
  \ifx\mytempa\mytempb
    \expandafter\endgroup\expandafter\@secondoftwo
  \else
    \expandafter\@firstofone
  \fi
  {%
    \def\mytempb{}%
    \expandafter\endgroup\ifx\mytempa\mytempb
      \expandafter\@secondoftwo
    \else
      \expandafter\@firstoftwo
    \fi
    {#1}%
  }{#2}~foo(#2)%
}%
\makeatother

\parindent=0ex
\parskip=\bigskipamount

\begin{document}

``Default value'' or empty optional argument  or no optional argument will be given---%
\verb|\foo{hi}|:\\
\foo{hi}

``Default value'' or empty optional argument  or no optional argument will be given---%
\verb|\foo[Default value]{hi}|:\\
\foo[Default value]{hi}

``Default value'' or empty optional argument  or no optional argument will be given---%
\verb|\foo[]{hi}|:\\
\foo[]{hi}

``Default value'' or empty optional argument  or no optional argument will be given---%
\verb|\foo[{}]{hi}|:\\
\foo[{}]{hi}

A nice optional argument will be given---%
\verb|\foo[hello]{hi}|:\\
\foo[hello]{hi}

\end{document}

enter image description here

As I said before: In common/usual situations I definitely prefer the \@dblarg-thing to any of these approaches.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.