是否有一种简单的方法可以追溯地将 \long 前缀添加到宏的定义中?

是否有一种简单的方法可以追溯地将 \long 前缀添加到宏的定义中?

根据第 20 章电子书,定义为“长”的宏(通过\long在 之前声明\def\<macroname>)允许使用\par标记(即段落)的参数。假设我遇到一个宏,它是不是“long”(例如\thanks,相当于 的标题\footnote)。我想允许其参数中包含段落,但不以任何其他方式更改宏的定义。显然,我可以查找含义并重新定义宏,这次将其定义为“long”。但是有没有简单的方法来添加前缀\long

答案1

如果确定要执行的命令\long是宏,那么这应该可以工作

\def\longpatch#1{\expandafter\getparts\meaning#1\longpatch
  \begingroup\edef#1{\long\def\noexpand#1\the\toks0 {\the\toks2}}%
  \scantokens\expandafter{\expandafter\endgroup#1}}
\def\getparts#1:#2->#3\longpatch{\toks0={#2}\toks2={#3}}

\def\x#1#2{a#1|#2b\hfil},使用\longpatch\x将使用\meaning\x生成的

macro:#1#2->a#1|#2b\hfil

因此\expandafter\getparts\meaning\x\longpatch将存储#1#2\toks0并将存储a#1|#2b\hfil到中\toks2。然后我们定义,其中\edef,相同\x以扩展为

\endgroup\long\def\x#1#2{a#1|#2b\hfil}

但问题是,后面的\x只是一串字符,所有字符的类别代码都是 12(空格的类别代码为 10),甚至反斜杠也是如此。我们通过扩展\x内部来解决这个问题,重新读取字符,就好像它们是从文件中输入的一样。当然,如果定义中的标记具有非普通类别代码,\scantokens这将不起作用。\x

(注意:我们#1还将其用作临时宏,这样它就不会干扰其他可能定义的宏;它是安全的,因为当\scantokens执行里面的 \endgroup 时,它的重新定义就会消失,就在#1再次重新定义之前。)

另一个“解决方案”是在非宏的参数中使用\endgraf而不是。\par\long

答案2

您不能追溯性地\long在定义中添加前缀(正如马丁所写,使用\long\let不起作用)。

这使得重新定义成为唯一的选择,而这总是很危险的。如果您确切地知道原始宏是如何根据预期参数定义的,并且您确定它不会进行任何 catcode 欺骗,那么就有可能进行破解:

\newtoks\patchtoks    % helper token register
\def\longpatch#1%     % worker macro
  {\let\myoldmac#1%
   \long\def#1##1{\patchtoks={##1}\myoldmac{\the\patchtoks}}}

\def\a#1{#1}          % original definition

\longpatch\a          % redefition

\a{\TeX\par text}     % test

\bye

这里的技巧是:这是忽略\the\patchtoks嵌入标记的出现的情况。\par

我会将这个答案保留为社区维基,因为它相当丑陋,也许其他人可以改进它。

相关内容