9

Is it possible to build a macro with an optional parameter enclosed in round brackets () rather than square brackets, where one can say for example:

\mymacro(){}{}

or

\mymacro{}{}
Joseph Wright
  • 259,911
  • 34
  • 706
  • 1,036
Chuang
  • 579

3 Answers3

13

A more traditional solution!

To build a command with any optional parameter, as you find in many of LaTeX's commands, you will need two things:

(a) a macro with delimited parameters

(b) a way to grab the first non-space token that follows the command

The first part is fairly easy using delimited argument macros, for example we can say

 \def\test(#1)#2#3{#1, #2, #3}

We can then call this macro as:

 \test(a){b}{c}

resulting in a,b,c

To define the () as an optional parameter, we effectively need to define the macro as a conditional a sort of a "yes-no" switch. If TeX finds the "(" bracket the "yes-code" will be called and if it finds only the normal arguments the "no-code" will be executed.

For this we can use the \@ifnextchar macro from the LaTeX kernel. You can say \@ifnextchar{char}{yes-code}{no-code} to test for (. The result then will depend on the token that follows. If this token is the same as the first argument, then the "yes-code" is executed, otherwise the "no-code" is executed. The first argument should be a single token (for instance a character). Spaces are ignored.

As for example we can redefine the LaTeX code for rule to accept an optional parameter in round brackets, rather than the traditional square brackets.

\def\Rule{\@ifnextchar(\@Rule%
        {\@Rule(\z@)}}

and the full example would look like:

\documentclass{article}
\begin{document}
\def\Rule{\@ifnextchar(\@Rule%
        {\@Rule(\z@)}}
\def\@Rule(#1)#2#3{%
 \leavevmode
 \hbox{%
 \setlength\@tempdima{#1}%
 \setlength\@tempdimb{#2}%
 \setlength\@tempdimc{#3}%
 \advance\@tempdimc\@tempdima
 \vrule\@width\@tempdimb\@height\@tempdimc\@depth-\@tempdima}}


A test \Rule(6.5pt){100pt}{1pt}

Another test \Rule{100pt}{1pt}
\end{document}
yannisl
  • 117,160
  • 1
    Very nice explanation! Of course, this has the same feature as LaTeX's own optional parameters: an option of the form (...(...)...) will only grab to the first parenthesis (except if inner ones are hidden within braces). – Bruno Le Floch Mar 07 '11 at 21:09
8

A simple xparse-based solution:

\documentclass{article}
\usepackage{xparse}
\NewDocumentCommand\mymacro{d()mm}{%
  \IfNoValueTF{#1}
    {}% Case where #1 is not given
    {}% Case where #1 is given
}

or if you want an empty #1 when it is not present:

\documentclass{article}
\usepackage{xparse}
\NewDocumentCommand\mymacro{D(){}mm}{%
  % Code here
}

(This works as d/D indicate an optional argument which is delimited.)

Joseph Wright
  • 259,911
  • 34
  • 706
  • 1,036
4
\documentclass{article}
\makeatletter
\def\mymacro{\@ifnextchar(\mymacro@i\mymacro@ii}
\def\mymacro@i(#1)#2#3{%
  #1:#2:#3}% the definition 
\def\mymacro@ii#1#2{%
  #1:#2}% the definition 
\makeatother
\begin{document}

\mymacro(foo){bar}{baz} -- \mymacro{BAR}{BAZ} 

\end{document}