最近有一些关于牙套的问题,特别是关于何时使用牙套(见这和那问题)。注意:我错误地说 SamB 是这两个问题的作者。
但还有其他方法可以获取像括号一样的标记列表。例如,
{ and
}
\let\bgroup{ and
\let\egroup}
\def\Egroup{\iffalse{\fi}} and
\def\Bgroup{\expandafter{\iffalse}\fi}
\def\bbgroup{\expandafter{\ifnum0=`}\fi} and
\def\eegroup{\ifnum0=`{\fi}}
当然,这不是一个完整的列表。如果能展示一下可以/应该使用每种方法的典型情况就好了。分享出现在您的宏中或您编写/阅读/使用过的包中的任何括号技巧...
我想每个答案都有一个 hack。
答案1
\iffalse{\fi
Dan Luecking 在 comp.text.tex 的帖子中发布了一个使用技巧的很好的例子将第一个字符大写附有完整解释。这是一个宏,以字符串为参数,并定义一个宏\Fcap
,该宏包含相同的字符串,但首字母大写(例如test
变为Test
):
\documentclass{article}
\def\FirstcapD#1#2\delimiter{%%% Delimited
\iffalse{\fi
\uppercase{\edef\Fcap{\iffalse}\fi#1}#2}}%
\begin{document}
\FirstcapD test\delimiter
\ttfamily\meaning\Fcap
\end{document}
表明这\Fcap
是macro:->Test
参数的大写版本test
。我在这里重现代码工作原理的解释。由于宏是分隔符,因此\FirstcapD test\delimiter
将使#1
=t
和#2
= est
。
第一个\iffalse{\fi
只是为了在 的定义中有平衡数量的括号\Firstcap
,但由于\iffalse
始终是假的,它会在扩展过程中消失。
然后\uppercase{\edef\Fcap{\iffalse}\fi#1}
用 执行#1 = t
。\uppercase{\edef\Fcap{\iffalse}\fi t}
因此 变为\edef\Fcap{\iffalse}\fi T
(t
已大写)。
在此阶段,我们有,\edef\Fcap{\iffalse}\fi Test}
因为#2
是est
。此代码在括号中不平衡,但这并不重要,因为\edef
它会随着读取而扩展。因为\iffalse
始终为假,所以\iffalse}\fi
会消失,剩下的只是\edef\Fcap{Test}
。
答案2
我将尝试按照 Hendrik 的建议进行解释\alignsafe@testopt
。该问题在 LaTeX 错误数据库中报告为amslatex/1834(现已修复)并进行后续处理乳胶/3040问题出现在以下情况下(在类似表格的环境中{aligned}
紧接着一个and ):&
\documentclass{article}
\usepackage{amsmath}
\makeatletter\let\alignsafe@testopt\@testopt\makeatother % reactivates the bug
\begin{document}
\begin{align}\begin{aligned}
&b=d%% Problem: First nonspace token found by lookahead was "&"
\end{aligned}\end{align}
\end{document}
原因是它{aligned}
提前查找可选参数,这会触发表格单元格的构建,从而导致错误。错误报告中提出的修复乳胶/3040比最终的结果更简单amsmath.sty
:
\def\@testopt#1#2{%
{\ifnum`}=0\fi
\@ifnextchar[%
{\ifnum`{=\z@\fi}#1}%
{\ifnum`{=\z@\fi}#1[#2]}%
}
但有一个缺点:它引入了一个伪元素{}
,在某些情况下可能会造成干扰。代码背后的想法如下:通过在 LaTeX 查找 之前打开括号[
,下一个&
不会立即结束父环境的当前单元,从而允许 LaTeX 测试它是否等于[
。比较时可以看到类似的效果
\begin{tabular}{c|c}a\meaning&b\end{tabular}
和
\begin{tabular}{c|c}a{\meaning&}b\end{tabular}
和仅在此处\ifnum`}=0\fi
,\ifnum`{=\z@\fi
这样\def
就不会抱怨其内容在括号中不平衡。在扩展期间,它们会消失。
最终的版本amsmath.sty
更加复杂,因为它用和替换了之前代码中的显式{
和,从而避免了前面提到的缺点。代码如下:}
\iffalse{\fi
\iffalse}\fi
\def\alignsafe@testopt#1#2{%
\relax\iffalse{\fi\ifnum`}=0\fi
\@ifnextchar[%
{\let\@let@token\relax \ifnum`{=\z@\fi\iffalse}\fi#1}%
{\let\@let@token\relax \ifnum`{=\z@\fi\iffalse}\fi#1[#2]}%
}
更微妙的是,使用\ifnum0=`{\fi
代替是\iffalse{\fi
行不通的。这可以通过以下事实来说明:
\documentclass{article}
\begin{document}
\begin{tabular}{cc}
a\ifnum`{=0\fi&\ifnum`}=0\fi b\\
\end{tabular}
\end{document}
编译而
\documentclass{article}
\begin{document}
\begin{tabular}{cc}
a\iffalse{\fi&\iffalse}\fi b\\
\end{tabular}
\end{document}
不会。原因在附录 D(“肮脏的伎俩”)中解释。TeXbook第 385 页(另请参阅TeX 按主题分类,§10.4):当\iffalse{\fi
扩展时,它会增加主计数器,而\ifnum0=`{fi
扩展不会,并且这个主计数器用于对齐事宜(另一个计数器是平衡计数器,它同样被两者欺骗)。扩展\ifnum0=`{fi
不会增加主计数器的原因是其中没有明确的括号,因为\ifnum0=
TeX 期望一个数字,因此`{
会转换为数字,而不是反引号后跟括号(事实上,真实的`{
不增加主计数器的原因是,当 TeX{
在反引号后读取时,它会增加主计数器,但当它评估时`{
,它会减少它,所以就好像计数器没有改变一样。对于 也是如此,\romannumeral-`{\space
但对于 则不然\@gobble`{
(因为 TeX 在 之后不处于“读取数字”模式\@gobble
)。
以下是TeXbook(第 385 页)显示当发生以下情况时哪个计数器会发生什么情况:
答案3
再次使用这个\iffalse{\fi
技巧。
给定一个形式为 的列表\def\foo{\elt{world}\elt{Hello,}\elt{!}}
,我想\popright\foo
从中删除最后一个元素\foo
(即,产生与 相同的结果\def\foo{\elt{world}\elt{Hello,}}
)。这可以在线性时间内完成。想法是以 开始\edef\foo{\iffalse}\fi
,以 结束定义\iffalse{\fi}
。在这两者之间,每个元素都包裹在 中\unexpanded
。
\catcode`\@=11
\long\protected\def\popright#1{%
\edef#1{\iffalse}\fi
\expandafter\expandafter
\expandafter\popright@aux
\expandafter\@gobble #1X}
\long\def\popright@aux#1#2{%
\ifx X#2%
\expandafter\popright@end
\fi
\unexpanded{\elt{#1}}%
\popright@aux}
\long\def\popright@end#1\popright@aux{\iffalse{\fi}}
\catcode`\@=12
最后一个元素的检测方法是,注意它后面不是\elt
,而是X
。在 LaTeX3 代码中,我决定用 代替{?\iffalse{\fi}\@gobblethree}
,X
并用 替换相对昂贵的\ifx
测试\@gobble#2
,但这只会带来一点速度上的提升,而且代码更加晦涩难懂。[\seq_pop_right:NN
参见source3.pdf
]
值得注意的是,DEK 本人在 TeXbook(附录 D)中提到,在 TeX 中做到这一点可能无法实现。\unexpanded
完整的解决方案需要 eTeX。
答案4
我发现一个有用的括号技巧是我的这个答案用于排版连分数,语法比
1+\cfrac{1}{2+\cfrac{1}{3+\cfrac{1}{4}}}
这需要跟踪嵌套;使用
\xcontfrac{1;2,3,4}
看起来相当简单。这个想法是循环遍历列表2,3,4
并构建一个包含
1+
\noexpand\cfrac{1}\iftrue{\else}\fi 2+
\noexpand\cfrac{1}\iftrue{\else}\fi 3+
\noexpand\cfrac{1}\iftrue{\else}\fi 4
另一个包含
\iffalse{\else}\fi
\iffalse{\else}\fi
\iffalse{\else}\fi
(为了清楚起见添加了缩进)以便将这些粘贴到标记列表中并将结果提供给\edef
构建所需的标记列表,以便最终\cfrac
可以完成其工作。