Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

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?

share|improve this question
3  
Sheesh, three answers in thirty seconds. Nice question, Tomas. –  Ryan Reich Nov 11 '10 at 9:55
add comment

5 Answers

up vote 15 down vote accepted

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}
share|improve this answer
    
Brilliant! This works exactly as I want it to, and is simple enough to understand =) Thanks! –  Tomas Lycken Nov 11 '10 at 10:27
add comment

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}
share|improve this answer
    
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.) –  Hendrik Vogt Nov 11 '10 at 13:50
    
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 '10 at 13:57
add comment

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.

share|improve this answer
add comment

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.

share|improve this answer
add comment
\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]}}
share|improve this answer
    
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... –  Tomas Lycken Nov 11 '10 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. –  Harald Hanche-Olsen Nov 11 '10 at 12:09
    
+1 for the idea to use repeated \newcommand s. –  Hendrik Vogt Nov 11 '10 at 16:39
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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