我无意中发现(这里) 中用以分隔的可选参数内的右方括号[ ]
可能会导致问题。
以下是说明该问题的示例代码:
\documentclass{article}
\begin{document}
\tableofcontents
\section[Square brackets ([ and ])]{Square brackets: [ and ]}
% bad: the TOC now has "Square brackets ([ and", and the actual title is a mere ")".
Some text.
\section[{Square brackets ([ and ])}]{Square brackets: [ and ]}
% good (works as originally intended)
Some text.
\end{document}
用作可选(以及非可选)参数的文本包含]
,并且此字符不应该用作可选参数终止符,而是用作匹配的方括号/圆括号。也许这是应该的,但问题是如何最好地规避任何潜在问题。将其括在一个组中可以工作,但我不知道这样做是否有其他副作用(也许在这里不是,\section
但希望其他人有更好的例子说明分组可能导致问题)。除了写之外没有(明显的)方法,[{ }]
因为[
和]
通常不被转义。这[
和]
是普通字符并承担句法功能(作为可选参数分隔符)会造成这种潜在的问题情况。
简单地将整个可选参数括在一个组 ( { }
) 中是一个好的解决方案吗?还有其他/更好的方法吗?
答案1
LaTeX 的可选参数,即 TeX 的宏参数(分隔和非分隔)
LaTeX 中可选参数(即可以使用或不使用的参数)的概念是不是TeX 的解析和执行直接支持。TeX 宏总是期望具有相同数量的参数,并使用相同的语法来界定参数。
LaTeX 中的可选参数是通过从没有参数的宏开始实现的,首先预查下一个标记是什么(例如寻找 a[
或 a *
),然后内部调用一个可以准确解析这样的 a[
或*
并期望它存在的 TeX 宏。
对于 TeX 解析器,只有两种类型的宏参数:“无界”和“有界”参数。“无界”参数的正常情况定义如下:
\def\test#1#2#3{... do something with #1 #2 #3}
最多可以有 9 个参数(即,此外还可以有#4
... )。#9
如果在参数说明符之前或之后放置任何内容,#<digit>
那么我们就处于“分隔”参数的情况,例如,
\def\test*#1[#2]#3foo{... do something with #1 #2 #3}
此处,第二个参数\test
应被and*
包围,第三个参数后必须跟字符串。但这里没有任何“可选”的东西,这些组件现在始终是必需的,如果不是,则会产生低级 TeX 错误,要么是“使用的不符合其定义”,要么是“参数失控”。[
]
foo
\test
TeX 的参数解析规则
TeX 中的括号(或者更准确地说是 catcode 为 1 和 2 的字符,通常是开括号和闭括号)在解析宏的参数时起着特殊作用。TeX 在解析参数时会跟踪它们,以确保它们是平衡的。
如果你有一个带有无分隔参数的宏,例如,
\def\test#1{\def\result{#1}}
然后 TeX 的解析器在执行时执行以下操作\test
:
- 如果之后的第一个标记
\test
不是 catcode 为 1 的字符(通常{
),那么下一个标记就成为参数。 - 否则,它会进一步扫描并仅查看 catcode,直到看到 catcode 为 1 和 2 的标记数量相等,换句话说,一组平衡的大括号组。
- 然后它会剥去外部的标记集,剩余的材料将成为参数。换句话说(在正常的 TeX catcode 生效的情况下),参数周围的括号不会成为参数的一部分。但是,里面的任何其他括号都会保留下来。
例子:
\test{A} \show\result
\test{{A}} \show\result
这现在给出了
> \result=macro:
->A.
l.4 \test{A} \show\result
?
> \result=macro:
->{A}.
l.5 \test{{A}} \show\result
即外面的括号消失了。
TeX 仅针对 catcode 为 1 和 2 的标记执行上述操作。如果您使用分隔参数的概念(带有其他 catcode 的字符),则不会发生平衡。例如
\def\test[#1]{\def\result{#1}}
Now\test
是一个宏,它期望后面跟着一个[
,并且它的参数在解析下一个时(通常)结束]
。在参数内部,TeX 仍然需要 catcode 1 和 2 的匹配标记,但它不关心括号:只要没有任何 catcode 1 的标记没有匹配的 catcode 2 的标记,第一个括号就会结束参数。
因此,执行时会发生以下情况\test
:
- TeX 期望一个
[
,如果不是,则会抱怨 - 然后,它开始解析参数,寻找
]
同一括号级别上的下一个参数(或者更确切地说,在同一级别上,匹配具有 catcode 1 和 2 的标记)。 - 一旦发现,
]
其间的任何东西都将成为参数,界定参数的标记将被丢弃。 - 但是,最后一句话并不完全正确:当看到结束分隔符时,实际发生的情况如下:TeX 移动到与在无分隔符情况下找到参数结尾时相同的状态。这意味着它现在查看候选参数,如果它以 catcode 1 标记开头并以 catcode 2 标记结尾,它将同时删除这两个标记。
使用上述定义的例子:
\test[A] \show\result
\test[{A}] \show\result
这次我们得到:
> \result=macro:
->A.
l.10 \test[A] \show\result
?
> \result=macro:
->A.
l.11 \test[{A}] \show\result
因此,总而言之,括号组(或者更确切地说是 catcode 1 和 2 标记组)是向 TeX 扫描仪隐藏某些内容的方法,否则这些内容将被误解。如果您确实希望将可选参数括在一组括号中(在解析后),则需要编写[{{...}}]
。
现在你可能想知道为什么 LaTeX 没有分别制作[
catcode ]
1 和 2 的标记。答案是:
- 它们将无法在普通文本中使用
- 更重要的是 TeX 扫描仪只查看 catcodes,因此你可以将其混合搭配,
{
一切]
都将是一样的
例子:
\def\test#1{\def\result{#1}}
\catcode`\[=1
\catcode`\]=2
\test{[{A]}] \show\result
这可行并给出:
> \result=macro:
->[{A]}.
l.8 \test{[{A]}] \show\result
LaTeX2e 可选参数的结论
总而言之,人们可以声称 LaTeX 中的可选参数确实正确地指定为[{
(open) 和}]
(close),并且[...]
如果您不必向扫描仪隐藏任何内容(大多数情况下都是如此),这只是一种方便的快捷方式。这样做不会产生任何副作用(只要使用序列 --- 当然,如果您在参数中间使用括号,则它们不会被剥离,并且会产生副作用)。
实际上,莱斯利恰恰相反:他说,[...]
界定可选参数,以防遇到麻烦,请使用[{...}]
。
xparse
通过包在 LaTeX3 中提供可选参数
xparse
使用的实现包具有expl3
更精细的扫描器,并且能够正确处理可选参数内的嵌套括号,即通常不需要将其向扫描器隐藏。
例子:
\documentclass{article}
\usepackage{xparse}
\DeclareDocumentCommand\test{om}{\def\result{#1||#2}}
\test{A} \show\result
\test[B]{A} \show\result
\test[B and [C]]{A} \show\result
执行此代码时,我们得到以下结果:
> \result=macro:
->-NoValue-||A.
l.6 \test{A} \show\result
?
> \result=macro:
->B||A.
l.7 \test[B]{A} \show\result
?
> \result=macro:
->B and [C]||A.
l.8 \test[B and [C]]{A} \show\result
当然,这仅在括号匹配时才有用,例如,在\test[B] and C]]{A}
扫描仪无法弄清楚您指的B] and C]
是可选参数的内容而不仅仅是的情况下B
。因此,在这种情况下,仍然需要使用[{...}]
来隐藏不平衡的材料。