根据第 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
我会将这个答案保留为社区维基,因为它相当丑陋,也许其他人可以改进它。