44

I'd like to define a command that takes 1, 2 or 3 arguments, and where the 2nd gets the same value as the 1st if it is not defined. I tried

\newcommand\codefrom[3][#1][Matlab]{ ... }

but I get an error saying

! Illegal parameter number in definition of \codefrom.
<to be read again>
                   1
l.57 \newcommand{\codefrom}[3][#1][
                                                              Matlab]

Is there any way to accomplish what I want to do?

3
  • 4
    Sheesh, three answers in thirty seconds. Nice question, Tomas.
    – Ryan Reich
    Nov 11, 2010 at 9:55
  • In 2020, you may want to consider pulling the now best answer (?) up to help people landing here from search. Thanks!
    – Raphael
    Jun 17, 2020 at 9:04
  • I'm reluctant to making such a change without verifying that it actually works - and since I no longer work with TeX (this question is almost 10 years old!) I have no reason (or incentive...) to spend time on this. Isn't that what votes are for? Jun 17, 2020 at 13:41

5 Answers 5

24

You can't accomplish this with one \newcommand. There you can define one optional parameter, and it has to be the first one. There is the twoopt package that defines \newcommandtwoopt for two optional parameters, but I don't think it can handle what you want to achieve. I first thought you would need to use TeX's \def, but here's a solution that's completely free of any \defs and @s:

\documentclass[12pt]{report}
\newcommand\storefirst{}
\newcommand\storesecond{}
\newcommand\codefrom[1]{\renewcommand\storefirst{#1}\codefromi}
\newcommand\codefromi[1][\storefirst]{\renewcommand\storesecond{#1}\codefromii}
\newcommand\codefromii[1][Matlab]{\storefirst, \storesecond, #1}
\begin{document}
\codefrom{first}\par
\codefrom{first}[second]\par
\codefrom{first}[second][third]
\end{document}
3
  • Brilliant! This works exactly as I want it to, and is simple enough to understand =) Thanks! Nov 11, 2010 at 10:27
  • I'm having a problem with this solution. If you type \codefrom{first} Hello you'll get something like "first, first, MatlabHello" (note the missing space). The same with the other examples. Is there a easy solution to this? Otherwise, it is indeed a very nice answer. Nov 26, 2016 at 21:34
  • @PedroSánchezTerraf Use the following snippet (~ is a non-breaking space): \codefrom{first}~Hello
    – Rauni
    Feb 25, 2017 at 11:05
15

Here is a solution based on xparse:

\documentclass{minimal}

\usepackage{xparse}

\NewDocumentCommand\codefrom{moO{Matlab}}{%
  \IfValueTF{#2}{%
    \codefromaux{#1}{#2}{#3}%
  }{%
    \codefromaux{#1}{#1}{#3}%
  }%
}
\NewDocumentCommand\codefromaux{mmm}{%
  1 = #1, 2 = #2, 3 = #3%
}

\begin{document}

\codefrom{a}\par
\codefrom{a}[b]\par
\codefrom{a}[b][c]

\end{document}
3
  • Very interesting. Could you elaborate a bit on the moO? (I think I can guess what it means, but still some confirmation would be great.) Nov 11, 2010 at 13:50
  • 1
    m = mandatory argument, o = optional argument (square brackets) with no default value, O = optional argument (square brackets) with default value as given. There was recently a question about trying to be able to use the value of one argument directly as the default for a second in xparse: currently, I've not come up with a good way to do it, I'm afraid.
    – Joseph Wright
    Nov 11, 2010 at 13:57
  • 3
    With the latest xparse (not yet on CTAN) one can replace the signature by m O{#1} O{Matlab} to directly have the second argument default to the first (no need for \IfValueTF then (basically Patrick Häcker's answer is now actually supported). Feb 12, 2017 at 6:32
15

The question is old, but as all answers are unnecessary complicated, I want to provide a simpler solution using xparse's possibilities directly.

\documentclass{minimal}
\usepackage{xparse}
\begin{document}
    \NewDocumentCommand{\codefrom}{mO{#1}O{Matlab}}{#1, #2, #3}
    \codefrom{a} \quad \codefrom{a}[b] \quad \codefrom{a}[b][c]
\end{document}

The result is enter image description here. The first argument is mandatory, the second and third ones are Optional. The second argument's default value is the first argument, the third argument's default value is Matlab.

2
  • It might be useful for others to know that it appears that only earlier arguments may be referenced this way. For example, I can't get this to work: {O{#2}m}. That tries to insert a literal "#2". Feb 15, 2016 at 8:06
  • @EvgeniSergeev with the latest xparse (not yet on CTAN) what you describe is supported (in fact, until now we had not explicitly supported the syntax described in the answer). Feb 12, 2017 at 6:33
12

Yes, but you have to "roll your own", so to speak. (You should note that even the sort of optional arguments you see in various core LaTeX macros, like the ones which take two optionals, are not supported by the \newcommand syntax).

The basic mechanism is to use \@ifnextchar to figure out whether there is an optional argument, filling it in if not, and then calling an internal macro which doesn't take an optional argument. In your case the following seems reasonable (note: written on the fly):

\makeatletter
\newcommand{\codefrom}[1]{
 \@ifnextchar[
   {\codefrom@firstinner{#1}}
   {\codefrom@firstinner{#1}[#1]}
}

\def\codefrom@firstinner#1[#2]{
 \@ifnextchar[
   {\codefrom@secondinner{#1}{#2}}
   {\codefrom@secondinner{#1}{#2}[Matlab]}
}

\def\codefrom@secondinner#1#2[#3]{
 ...
}
\makeatother

As you can see, at each step it checks for a [ and, if it is not present, fills in the argument with the desired default. The last step has all the arguments, and continues the process.

7
\documentclass{article}
\makeatletter
\newcommand{\codefrom}[1][]{\def\c@defr@m{#1}\c@defrom}
\newcommand{\c@defrom}[2][\c@defr@m]{\message{[\c@defr@m,#1,#2]}}
\makeatother
\begin{document}
\codefrom{a}
\codefrom[a]{b}
\codefrom[a][b]{c}
\end{document}

Edit: An alternative, if it is important for you to access the parameters as #1–#3:

\newcommand{\codefrom}[1][]{\def\c@defr@m{#1}\c@defrom}
\newcommand{\c@defrom}[1][\c@defr@m]
  {\expandafter\@codefrom\expandafter{\c@defr@m}{#1}}
\newcommand{\@codefrom}[3]{\message{[#1,#2,#3]}}
3
  • This is great, and almost what I need =) The output now shows [,,a] [a,a,b] [a,b,c], but I'd like it to be [a,a,a] [a,a,b] [a,b,c] (or with some permutation of it). Also, I don't see where the \c@defr@om command comes from, but your MWE compiles on my machine... Nov 11, 2010 at 10:19
  • What \c@defr@om command? I neither use nor define one. I admit these names are hard to read and keep apart, though. I guess I have been badly influenced by lots of other (La)TeX code that uses the s@me s@rt of n@ming convention. Anyway, having #3 be the default value of #1 and #2 was not in your original specification. I don't have the time right now to come up with a good way to do that, but it is probably most easily achieved in a final processing step – if you're happy with a solution that treats [] the same way as if the optional parameter had been skipped. Nov 11, 2010 at 12:09
  • +1 for the idea to use repeated \newcommand s. Nov 11, 2010 at 16:39

You must log in to answer this question.

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