例如,我会写\defpoint [option] (1,2) {A}
而不是
\defpoint[option](1,2){A}
。构建这种宏的最佳方法是什么?
只有参数 [...] 是可选的,如果参数之间允许有空格,则不需要空格(强制性的?)。
如果用户犯了一个语法错误,记录下来可能会很有趣,但是该怎么办呢?
答案1
使用标准 LaTeX2e 方法,无需额外努力即可实现此目的:
\makeatletter
\long\def\defpoint{%
\@ifnextchar[%]
{\defpoint@aux@i}
{\defpoint@aux@i[]}%
}
\long\def\defpoint@aux@i[#1]{%
\@ifnextchar(%)
{\defpoint@aux@ii{#1}}
{\ERROR}% As the ( ... ) part is not optional
}
\long\def\defpoint@aux@ii#1(#2)#3{\showtokens{#1:#2:#3}}
\makeatother
\defpoint [option] (1,2) {A}
这对于三个论点有三个不同的原因!
对于第一个参数(在方括号中),TeX 会跳过控制序列后的空格,程序员对此无能为力:这里始终允许使用空格(忽略相当尴尬的 catcode 技巧)。
对于括号中的可选参数,\@ifnextchar
会故意跳过它拾取的任何空格,因此会忽略]
和之间的任何空格(
。可以创建一个\@ifnextchar
不跳过空格的版本(实际上,这比当前实现更容易)。请注意,如果没有该参数,那么 TeX无论如何[...]
都会跳过后面的空格。\defpoint
最后,再次以标准方式抓取强制参数使用 TeX 的逻辑。如果不采取其他预防措施,#3
这里将被抓取为下一个 <balanced text>,可以更简单地看到类似
\def\test#1#2#3{\showtokens{#1:#2:#3}}
\test {A} {B} {C}
答案2
xparse
默认情况下允许这样做。请参阅章节1.2 间距和可选参数的xparse
文档:
TeX 会查找函数名称后的第一个参数,而不考虑中间是否有空格。强制参数和可选参数都是如此。
除非某些特定的参数格式,这是一个最小的例子:
\documentclass{article}
\usepackage{xparse}% http://ctan.org/pkg/xparse
\NewDocumentCommand{\mycmd}{o d() m}{%
1: #1; 2: #2; 3: #3
}
\begin{document}
\mycmd[abc](ijk){xyz} \par
\mycmd [abc](ijk){xyz} \par
\mycmd[abc] (ijk){xyz} \par
\mycmd[abc](ijk) {xyz} \par
\mycmd [abc] (ijk){xyz} \par
\mycmd[abc](ijk) {xyz} \par
\mycmd[abc] (ijk){xyz}
\end{document}
可以通过布尔测试对参数执行语法错误,例如\IfNoValueTF
。
答案3
除了其他答案之外,我想指出的是,使用 LaTeX 的标准宏定义机制,您可以准备选项,而不必将其余参数传递给辅助宏\defpoint@option
。
该宏\defpoint
不使用下一个标记,而是将它们留在输入流中。如果没有选项,它只会在标记流的开头添加默认选项,然后调用\defpoint@option
,这会使用所有参数。
\makeatletter
\newcommand\defpoint[1][option]{%
\defpoint@option[#1]% partial application
}
\def\defpoint@option[#1](#2,#3) #4{% There _has_ to be a space before #4
Look ma: #1, #2, #3, and #4.
}
\makeatother
\defpoint(1,2) {A}
\defpoint[blah](1,2) {A}
\defpoint(1,2){A} % doesn't work