任何命令序列都可用于界定宏参数。即使尚未定义的命令序列也是如此。例如:
\def\YY#1,#2\@@{#1,#2}
\YY123,2345\@@
即使宏\@@
尚未定义,也会顺利工作。
在 LaTeX 源代码中有一个不寻常的宏,其中宏本身的名称被用作分隔符。简而言之,下面的示例可以正常工作,没有任何错误。
\def\XX#1,#2\XX{#1,#2}
\XX12,13\XX
这是否提供了任何内存或速度优势或任何其他优势?
实验的简短 MWE 如下:
\documentclass{article}
\begin{document}
\makeatletter
\def\YY#1,#2\@@{#1,#2}
\YY123,2345\@@
\def\XX#1,#2\XX{#1,#2}
\XX12,13\XX
\makeatother
\end{document}
答案1
由于没有定论,我决定再次在 TeXBook 中寻找解释,但一无所获。然而,这引出了另一个例子。在练习 24.6 的答案中,Knuth 展示了如何\cs
使用 将控制序列变成隐式空间\futurelet
。
\def\\#1\\{}\futurelet\cs\\ \\
你可以尝试用最少的
\tt
\def\\#1\\{}\futurelet\cs\\ \\
% example
a.\cs b.c
\def\empty{}
% Trying with an empty macro
a.\empty b.c
\bye
显然,下一个停靠港是原来的TeX 源代码本身。由于 Pascal 是一种类型化语言,我怀疑以这种方式使用分隔符将节省一个字符串名称。
字符串定义在第 4 部分,字符串处理。所有字符串(事实上 TeX 中的几乎所有内容)都转换为整数并进行索引。就 TeX 而言,原始宏的名称或错误消息的字符串没有什么不同,它们都放在 中str_pool
。
当原始的 WEB 系统程序 TANGLE 处理 TEX.WEB 文件时,它会输出一个 Pascal 程序 TEX.PAS(现在是 CWEB 和 C 程序)以及一个字符串文件 TEX.POOL,其中包含所有使用的字符串。INITEX 程序读取后者文件,其中每个字符串都以两位十进制长度的形式出现,后面跟着字符串本身,信息记录在 TEX 的字符串内存中。INITEX 稍后会生成一个二进制格式的文件,随后 TeX 引擎可以高速读取该文件。(您可以在发行版中搜索 TEX.POOL 文件来查看它)。
考虑到 LaTeX 的历史,以这种方式使用分隔符是有意义的,因为它保留了一个字符串。
我没有仔细研究过在正常排版操作(即没有通过 INITEX 传递源)期间对用户定义字符串的扫描例程。很可能使用了相同的机制,而且显然这将节省一些内存空间,无论节省的内存空间有多小。
是否应该以这种方式使用分隔符是有争议的,因为它往往会使代码变得模糊,也许这就是它们通常不出现在包中的原因。相反,应该为此保留两个或三个字符串名称。如果有一个例外,我会说它是类似于 Knuth 定义的宏:
\def\\#1\\
它具有一种对称性,可以说具有一定的美!
特别感谢所有发表评论的人,特别是感谢 Lev Bishop 提供的额外示例。
答案2
在我看来,它确实提供了一些优势,特别是为了捕获重复使用无参数宏的内容。一个实际的例子来自enumerate
自动对环境中的物品进行排序在列表环境中重复使用\item
提供了一种方法来描述(或参数化)所使用的“参数”。一般来说,列表中的项目具有以下形式
%...
\item <some stuff>
\item <some more stuff>
%...
或者更恰当地写成
%...
\item <some stuff> \item
<some more stuff>
%...
可以使用以下方式捕获
\def\item#1\item{<do something with #1>}
当然,你必须找到一种方法来管理列表的结尾,因为最后一项没有明显的结尾\item
来匹配新定义的参数文本\item
。这可以通过\item
在捕获其全部内容后附加到环境末尾来实现environ
。更多详细信息请参阅链接文章。