偶尔我会尝试理解 TeX 的工作原理...到目前为止成功了,但有时我会设法定义一个宏。例如,
\def\appendto#1#2{\expandafter\def\expandafter#1\expandafter{#1#2}}%
可以通过例如排版文本
\def\txt{}
\appendto{\txt}{Hello}
\appendto{\txt}{ }
\appendto{\txt}{world!}
%% Expand it.
\txt
那么我的问题是,如何定义相应的插入宏?相应的插入宏应该扩展为
你好世界!
当输入为
\def\txt{}
\insert{\txt}{world!}
\insert{\txt}{ }
\insert{\txt}{Hello}
\txt
答案1
基于宏观的方法
\def\prependto#1{%
\expandafter\prependtohelper\expandafter{#1}#1%
}
\long\def\prependtohelper#1#2#3{%
% #1: contents of macro
% #2: macro
% #3: text for prepending
\def#2{#3#1}%
}
% Testing
\def\txt{}
\prependto{\txt}{world!}
\prependto{\txt}{ }
\prependto{\txt}{Hello}
\immediate\write16{\meaning\txt}
\csname @@end\endcsname\end
评论:
我添加了
\long
宏\prependhelper
,用于读取插入的文本。然后文本可能还包含\par
标记(空行、整个段落)。基于宏的方法的缺点是
#
token 会带来麻烦。以下基于 token 或 e-TeX 的方法解决了这个问题。
基于代币的方法
\def\space{ }% already defined in plain TeX and LaTeX
\long\def\prependto#1#2{%
\begingroup
\toks0={#2}%
\toks0=\expandafter{\the\toks0\expandafter\space#1}%
\xdef\prependhelper{\the\toks0}%
\endgroup
\let#1\prependhelper
}
\def\txt{}
\prependto{\txt}{world!}
\prependto{\txt}{ }
\prependto{\txt}{Hello}
\immediate\write16{\meaning\txt}
\csname @@end\endcsname\end
评论:
\edef
如果在(或全局工作)中扩展令牌寄存器,则有一个特殊之处\xdef
。令牌寄存器是输出,但输出令牌不会进一步扩展。该组的目的是将令牌寄存器 0 的更改保持在本地。全局宏
\prependhelper
将宏的新定义传输到组外。宏定义使用了一些扩展技巧。在标记寄存器赋值看到左花括号之前,它仍然会扩展宏,因此
\expandafter
可以转到那里,而不需要从赋值之前开始。\toks0\expandafter\space
是另一个扩展技巧。如果 TeX 解析数字,则它会扩展直到找到非数字。如果空格作为结尾,则忽略该空格。
如果可以使用临时寄存器(如)\toks@
,则定义可以变得更小(使用\makeatletter
或\catcode`@=11
):
\long\def\prependto#1#2{%
\toks@={#2}%
\toks@\expandafter{\the\expandafter\toks@ #1}%
}
基于 e-TeX 的方法
e-TeX 扩展提供了\unexpanded
,可用于避免使用令牌寄存器的规避:
\long\def\prependto#1#2{%
\edef#1{\unexpanded{#2}\unexpanded\expandafter{#1}}%
}
\def\txt{}
\prependto{\txt}{world!}
\prependto{\txt}{ }
\prependto{\txt}{Hello}
\immediate\write16{\meaning\txt}
\csname @@end\endcsname\end
评论:
- 再次,
\expandafter
before\unexpanded
可以被删除,因为 e-TeX 会一直扩展 after\unexpanded
直到找到打开的花括号。