3

I have been reading this page on defining commands with *-options. I have got completely stuck, though. I'd like the following.

A command with an optional argument and a star option.

Optional Argument: \cmd{b} --> N_b and \cmd[a]{b} --> N_b(a)

Starred Argument: \cmd*{b} --> N^*_b and \cmd*[a]{b} --> N^*_b(a)

I'm not sure if one can use the star argument as well as an optional argument, though.

These are my attempts, but they don't work.

\makeatletter
\newcommand{\cmd}[2]{%
    \@ifstar
    {%
        \ifthenelse{\equal{}{#1}}%
        {\NN^*_{#2}}%
        {\NN^*_{#2}(#1)}%
    }%
    {%
        \ifthenelse{\equal{}{#1}}%
        {\NN_{#2}}%
        {\NN_{#2}(#1)}%
    }
}
\makeatother

---------------

\makeatletter
\newcommand{\cmd}[2]{%
    \@ifstar
    \cmdStar[#1]{#2}%
    \cmdNoStar[#1]{#2}%
    \makeatother
}
\newcommand{\cmdStar}[2]{%
    \ifthenelse{\equal{}{#1}}
    {\NN_{#2}}
    {\NN_{#2}(#1)}
}
\newcommand{\cmdNoStar}[2]{%
    \ifthenelse{\equal{}{#1}}
    {\NN^*_{#2}}
    {\NN^*_{#2}(#1)}
}
3

You want first to distinguish whether the * appears or not.

\newcommand{\cmd}{N\@ifstar{^*\@cmd}{\@cmd}}

Then you define \@cmd

\newcommand{\@cmd}[2][]{%
  _{#2}%
  \if\relax\detokenize{#1}\relax
    % no or empty optional argument
  \else
    (#1)%
  \fi
}

Since we use @-commands, this should be preceded by \makeatletter and followed by \makeatother. One could dispense with @ in command names, though, by choosing “strange” names.

It's simpler with \NewDocumentCommand.

\NewDocumentCommand{\bettercmd}{som}{%
  N_{#3}%
  \IfBooleanT{#1}{^*}% add the asterisk
  \IfValueT{#2}{(#2)}% there is an optional argument
}

Notes.

  • s introduces a possible * variant; if the * appears, the command \IfBooleanTF{#1}{TRUE}{FALSE} will return TRUE, FALSE otherwise. Since we do nothing when there is no *, it's simpler to use \IfBoolean{T}{#1}{^*} that adds the superscript only if the * is present.
  • o introduces an optional argument; if no [ appears in the expected place, \IfValueTF{#2}{TRUE}{FALSE} will return FALSE, otherwise TRUE. Again, since we do nothing when the optional argument isn't present, it's simpler to use \IfValueT{#2}{(#2)}
  • m introduces a mandatory argument (between braces, as usual).

Test document

\documentclass{article}

\makeatletter
\newcommand{\cmd}{N\@ifstar{^*\@cmd}{\@cmd}}
\newcommand{\@cmd}[2][]{%
  _{#2}%
  \if\relax\detokenize{#1}\relax
    % no or empty optional argument
  \else
    (#1)%
  \fi
}
\makeatother

\NewDocumentCommand{\bettercmd}{som}{%
  N_{#3}%
  \IfBooleanT{#1}{^*}% add the asterisk
  \IfValueT{#2}{(#2)}% there is an optional argument
}

\begin{document}

$\cmd{b}+\cmd[a]{b}+\cmd*{b}+\cmd*[a]{b}$

$\bettercmd{b}+\bettercmd[a]{b}+\bettercmd*{b}+\bettercmd*[a]{b}$

\end{document}

enter image description here

1
  • Thanks, @egreg. I was fairly close with my commands, but I agree that using \NewDocumentCommand is the best way. I like that you can introduce ordered arguments and say if they're optional or not. I'll have to look into it some more for future use!
    – Sam OT
    Jun 20 at 8:03
2

With a current version of LaTeX you can use \NewDocumentCommand, which makes it very easy to write a command with starred version and optional arguments in one go.

(If you are using an older version of LaTeX, the following code will most likely work if you add \usepackage{xparse} to the preamble before \ExplSyntaxOn.)

\documentclass{article}

\ExplSyntaxOn
\NewDocumentCommand{\cmd}{som}
  {
    N
    \sb{#3}
    \IfBooleanT{#1}
      {
        \sp{*}
      }
    \IfValueT{#2}
      {
        (#2)
      }
  }
\ExplSyntaxOff

\begin{document}
unstarred $\cmd{b}$ and $\cmd[a]{b}$

starred $\cmd*{b}$ and $\cmd*[a]{b}$
\end{document}

unstarred N_b and N_b(a)//starred N^∗_b and N^∗_b(a)

\sb can be used as a replacement of _ for maths subscripts and \sp as replacement of ^ for maths superscripts.

If you are not too fond of \ExplSyntaxOn and \ExplSyntaxOff you can go with

\NewDocumentCommand{\cmd}{som}{%
  N_{#3}%
  \IfBooleanT{#1}{^{*}}%
  \IfValueT{#2}{(#2)}%
}

instead, where we need %s to avoid spurious spaces as usual.


For a more classical LaTeX2e approach I would suggest something like

\documentclass{article}

\usepackage{etoolbox}

\makeatletter
\newcommand*{\cmd}{\@ifstar{\cmd@i{*}}{\cmd@i{}}}
\newcommand*{\cmd@i}[1]{%
  \@ifnextchar[%]
    {\cmd@ii{#1}}
    {\cmd@ii{#1}[]}}

\def\cmd@ii#1[#2]#3{%
  N_{#3}%
  \ifblank{#1}{}{^{#1}}%
  \ifblank{#2}{}{(#2)}%
}
\makeatother

\begin{document}
unstarred $\cmd{b}$ and $\cmd[a]{b}$

starred $\cmd*{b}$ and $\cmd*[a]{b}$
\end{document}

First we check for the star with \@ifstar. In order to keep code repetitions down we pass the star on as the first argument of our helper command if it is present. If there is no star, we just pass on an empty argument. Then we go on to grab the optional argument. Again via a helper that inserts [] if there is no optional argument. Finally we can define the macro that does the actual typesetting as a macro that takes three arguments #1 is the star, [#2] is the optional argument in square brackets (or [] if none was supplied), #3 is the mandatory argument at the end.

Here I used etoolbox for \ifblank to check if the arguments are empty, which is not strictly speaking the same as not given.

If you prefer ifthen, you can remove the \usepackage{etoolbox} and add

\usepackage{ifthen}
\newcommand*{\ifblank}[1]{\ifthenelse{\equal{}{#1}}}

The

\newcommand{\cmd}[2]{%
    \@ifstar
    ...
}

didn't quite work as desired because such a definition first tries to grab two mandatory (braced) arguments and then after grabbing the arguments checks if a star follows.

For \cmd*{b} that would actually mean that #1 is * and #2 is b. For \cmd[a]{b} we'd get #1->[ and #2 -> a.

1
  • Thanks, @moewe. I was fairly close with my commands, but I agree that using \NewDocumentCommand is the best way. I like that you can introduce ordered arguments and say if they're optional or not. I'll have to look into it some more for future use! I spent way too long thinking about which answer to accept. I went with @egreg's purely because there was a little more explanation. But yours alone.
    – Sam OT
    Jun 20 at 8:04
1

Based on this answer you can do the following:

\documentclass{article}
\usepackage{ifthen}
\makeatletter
\newcommand{\cmd}[0]{\@ifstar{\@cmdA}{\@cmdB}} % Create \cmd which has *no* arguments, it only checks whether '*' was used or not and then uses \@cmdA resp. \@cmdB.

\newcommand{\@cmdA}[2][]{ %     \@cmdA is the starred version.
   \ensuremath{%                Make sure everything is in math mode.
      N^{\ast}_{#2\ifthenelse{% Type 'N^*_{bXXX}' ...
       \equal{#1}{}}{}{(#1)}%   ... where 'XXX' is '', if there is no optional argument, otherwise '(a)'.
      }%
   }%
}

\newcommand{\@cmdB}[2][]{%      \@cmdB is the unstarred version.
   \ensuremath{%                Do exactly the same like in \@cmdA, but without the asterisk.
      N_{#2\ifthenelse{%
         \equal{#1}{}}{}{(#1)}%
      }%
   }%
}
\makeatother

\begin{document}
   \cmd{b}   \quad   \cmd[a]{b}   \quad   \cmd*{b}   \quad   \cmd*[a]{b}
\end{document}

Result of the above code.


EDIT: '(a)' as argument of 'N', not of 'b'

After seeing Moewe's answer I recognized that (a) should not be the argument of b but of N, so here is an updated version of \@cmdA and \@cmdB:

\newcommand{\@cmdA}[2][]{ %     \@cmdA is the starred version.
   \ensuremath{%                Make sure everything is in math mode.
      N^{\ast}_{#2}\ifthenelse{% Type 'N^*_{b}XXX' ...
         \equal{#1}{}%
      }{}{(#1)}%                 ... where 'XXX' is '', if the optional argument is empty (default), otherwise '(a)'.
   }%
}

\newcommand{\@cmdB}[2][]{%      \@cmdB is the unstarred version.
   \ensuremath{%                Do exactly the same like in \@cmdA, but without the asterisk.
      N_{#2}\ifthenelse{\equal{#1}{}}{}{(#1)}%
   }%
}

producing Result of the above code.

1
  • Ah, I wasn't so far off! Thanks for the explanation of how the arguments to the base \cmd work. I ended up accepting another answer because it gave another option, but yours was very informative, thanks!
    – Sam OT
    Jun 20 at 8:06

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.