我想定义一个命令,根据我是否提供可选参数,该命令会扩展为两个完全不同的东西。例如,
\mycmd{normal} -> something(normal)
\mycmd[optional]{normal} -> anotherthing(normal, optional)
我怎样才能做到这一点?
答案1
如果您深入阅读 LaTeX 代码,您会发现很多这样做的例子。任何具有可选参数的命令实际上已经这样做了:实际上有两个不同的命令,调用哪个命令取决于是否使用可选命令调用它。TeX 技巧是使用\@ifnextchar[
。例如,
\def\mycmd{\@ifnextchar[{\@with}{\@without}}
\def\@with[#1]#2{hello #1, have you met #2?}
\def\@without#1{goodbye #1}
这里发生的情况是,TeX 解释器遇到\mycmd
宏并将其扩展(当然,会吞掉后面的空格)。然后它遇到的第一件事是对下一个字符的测试(顺便说一句,这是非破坏性的 - 只是观察下一个字符,而不是处理它)。如果它是一个方括号,那么它会将其放入\@with
流中并重新开始。 \@with
定义为接受两个参数,第一个参数用方括号括起来。因此,它匹配第一个可选参数和 TeX 流中的下一个东西。如果下一个字符不是方括号,则会将其\@without
放入流中。这需要一个(正常)参数。但由于\@with
和\@without
是两个完全独立的命令,它们可以对输入执行任何它们想执行的操作。
(注意:对于使用 定义的命令,\newcommand
如果带有可选参数,则会发生类似的事情,即两个命令\@with
和\@without
扩展为同一个命令,但 的可选参数\@with
会作为第一个参数传递给它,而 中的\@without
会传递默认值。但不是相当就像那样,但区别更多的是在编程的简洁性上。)
这是一个完全可编译的示例,包括神秘而又神秘的二人组\makeatletter
和\makeatother
:
\documentclass{article}
%\url{https://tex.stackexchange.com/a/314/86}
\makeatletter
\def\mycmd{\@ifnextchar[{\@with}{\@without}}
\def\@with[#1]#2{Hello #1, have you met #2?}
\def\@without#1{Goodbye #1.}
\makeatother
\begin{document}
\mycmd[Polyphemus]{Odysseus}
\mycmd{Circe}
\end{document}
生成:
你好,波吕斐摩斯,你见过奥德修斯吗?再见,喀耳刻。
答案2
LaTeX3 xparse 包旨在帮助构建采用复杂可选参数的宏。在本例中,你可以这样写
\usepackage{xparse}
....
\DeclareDocumentCommand \foo { o m } {%
\IfNoValueTF {#1} {%
\something {#2}%
}{%
\someotherthing {#1} {#2}%
}%
}
从内部来看,这与 Andrew 的答案的作用相同,但编码更直接/易读,也更容易扩展。想要另一个可选参数吗?只需o
在{ o m }
参数中添加另一个即可。(o
是“可选参数”和m
“强制参数”;有关更多信息,请参阅 xparse 文档。)
答案3
我想感谢收到的所有其他答案。但最近我变成了电子工具箱包,它提供了一个很好的抽象,可以在 LaTeX 级别精确地完成我想要做的事情(没有低级别的 TeX 技巧):
\usepackage{etoolbox}
\newcommand\mycmd[2][]{%
\ifstrempty{#1}{%
% something with #2
}{%
% some other thing with #1 and #2
}%
}
答案4
我通常只是使用ifthen
包来解决这个问题,通过测试可选参数是否为空。
\newcommand\mycmd@noargs{foo}
\newcommand\mycmd@withargs[1]{bar #1}
\newcommand\mycmd[1][]{
\ifthenelse{\equal{#1}{}}{
\mycmd@noargs{}}{
\mycmd@withargs{#1}}}
(请注意,由于它用作@
字母,\makeatletter
如果它在您的主 LaTeX 文件(而不是包)中,您需要在此定义之前使用,\makeatother
在其之后使用。)