用宏本身来限定宏参数

用宏本身来限定宏参数

任何命令序列都可用于界定宏参数。即使尚未定义的命令序列也是如此。例如:

\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。更多详细信息请参阅链接文章。

相关内容