带有和不带有可选参数的不同命令定义

带有和不带有可选参数的不同命令定义

我想定义一个命令,根据我是否提供可选参数,该命令会扩展为两个完全不同的东西。例如,

\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在其之后使用。)

相关内容