使用\Pop
来自的宏时TeX 按主题分类章节“对标记列表的操作:堆栈宏”,我明白了Missing control sequence inserted
。为什么?我照原样复制了它!书中有印刷错误吗?我的眼睛欺骗了我吗?
代码
\documentclass{article}
\usepackage{fontspec}% xelatex
\newtoks\tokenlist% initialize a token register
% Token List Functions
\def\Prepend#1#2{\toks0={#1}%
\edef\act{\noexpand#2={\the\toks0 \the#2}}%
\act}
\def\Pop#1#2{%
\edef\act{\noexpand\SplitOff\the#1%
\noexpand#2\noexpand#1}%
\act}
\def\SplitOff#1#2#3#4{%
\def#3{#1}#4={#2}}
% Add some tokens
\Prepend{{a}}{\tokenlist}% WORKS
\Prepend{{b}}{\tokenlist}% WORKS
\Prepend{{c}}{\tokenlist}% WORKS
\Pop{\tokenlist}{\front}
\showthe\tokenlist
\begin{document}
\null
\end{document}
答案1
标记(head:)
、(tail:)
和是(to:)
(into:)
基本的语法的部分。如果省略它们,宏肯定不会起作用。
书中的定义是
\def\Prepend#1(to:)#2{\toks0={#1}%
\edef\act{\noexpand#2={\the\toks0 \the#2}}%
\act}
\def\Pop#1(into:)#2{%
\edef\act{\noexpand\SplitOff\the#1%
(head:)\noexpand#2(tail:)\noexpand#1}%
\act}
\def\SplitOff#1#2(head:)#3(tail:)#4{\def#3{#1}#4={#2}}
并且它们假设字符串(head:)
、(tail:)
和永远不会出现在您管理的标记列表中。(to:)
(into:)
您对的定义\Prepend
仅适用于单个标记,但是\Pop
和\SplitOff
是完全错误的。
如果我将的定义\act
改为,TeX 会打印\show\act
\Pop
\SplitOff {c}{b}{a}\front \tokenlist
而 TeX 要求参数 处有一个控制序列#3
。让我们看看 Eijkhout 的定义会发生什么(再次使用\show\act
进行调试):
\def\Prepend#1(to:)#2{\toks0={#1}%
\edef\act{\noexpand#2={\the\toks0 \the#2}}%
\act}
\def\Pop#1(into:)#2{%
\edef\act{\noexpand\SplitOff\the#1%
(head:)\noexpand#2(tail:)\noexpand#1}%
\show\act}
\def\SplitOff#1#2(head:)#3(tail:)#4{\def#3{#1}#4={#2}}
\newtoks\tokenlist
% Add some tokens
\Prepend a(to:)\tokenlist
\Prepend b(to:)\tokenlist
\Prepend c(to:)\tokenlist
\Pop\tokenlist(into:)\front
我们得到:
> \act=macro:
->\SplitOff cba(head:)\front (tail:)\tokenlist .
符合以下语法\Splitoff
:argument #1
is c
、argument #2
isba
和argument #3
is \front
。
当然,使用 可以更轻松地完成此操作expl3
。
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\definetl}{m}
{
\tl_new:N #1
}
\NewDocumentCommand{\prepend}{mm}
{
\tl_put_left:Nn #2 { #1 }
}
\NewDocumentCommand{\pop}{mm}
{
\tl_set:Nx #2 { \tl_head:N #1 }
\tl_set:Nx #1 { \tl_tail:N #1 }
}
\ExplSyntaxOff
\definetl\tokenlist
% Add some tokens
\prepend{a}\tokenlist
\prepend{b}\tokenlist
\prepend{c}\tokenlist
\begin{document}
\pop\tokenlist\front
\verb|\front| $\to$ \front
\verb|\tokenlist| $\to$ \tokenlist
\end{document}
答案2
这可能是多余的和直接的,但如果有任何帮助,请解释一下书中的宏:
\def\Prepend#1(to:)#2{\toks0={#1}%
\edef\act{\noexpand#2={\the\toks0 \the#2}}%
\act}
这定义\Prepend
为首先扫描所有内容,直到字符串(to:)
为 #1,下一个标记为 #2。换句话说,当 TeX 遇到 时\Prepend
,它会扫描直到找到五个标记(
,t
,o
,:
,)
按该顺序排列 — 所有前面的标记都是 #1,之后的第一个标记是 #2。
所有这些标记都被 的定义替换\Prepend
,这导致 TeX 首先将#1
标记存储在 中\toks0
,然后定义\act
,然后遇到\act
并扩展它。
例如,如果#2
是\mytokenlist
,那么\act
将被定义为“ \mytokenlist = {\the\toks0 \the\mytokenlist}
”,只是两个表达式分别被和\the
的实际内容替换。您可以看到它如何有效地添加到 的前面。\toks0
\mytokenlist
#1
\mytokenlist
\def\SplitOff#1#2(head:)#3(tail:)#4{\def#3{#1}#4={#2}}
这定义了\SplitOff
一个宏,当 TeX 遇到时,它会将第一个标记扫描为#1
,之后的所有内容直到文字字符串(head:)
为#2
,之后的所有内容直到(tail:)
为#3
,之后的标记为#4
。然后它将所有这些替换为 的定义\SplitOff
,这会导致它定义#3
(为#1
)并设置#4
(为#2
)。
例如,
\SplitOff lots of tokens here(head:)\dest(tail:)\mytokenlist
TeX 会将 设置#1
为l
,然后#2
设置为ots of tokens here
,然后#3
设置为\dest
,然后#4
设置为\mytokenlist
。然后所有内容将被替换为:
\def\dest{l}\mytokenlist={ots of tokens here}
因此您可以看到,其效果是将其中的一部分拉出来放入l
其中lots of tokens here
,\dest
并将其余部分放入其中\mytokenlist
。
最后,
\def\Pop#1(into:)#2{%
\edef\act{\noexpand\SplitOff\the#1%
(head:)\noexpand#2(tail:)\noexpand#1}%
\act}
— 这只是使用上面的\SplitOff
宏。
例如,如果\mytokenlist
包含lots of tokens here
,则\Pop\mytokenlist(into:)\dest
首先定义\act
为
\SplitOff lots of tokens here(head:)\dest(tail:)\mytokenlist
l
正如我们之前看到的,它将from分离\mytokenlist
为 ,\dest
并将其设置\mytokenlist
为其余部分,这是\Pop
应该做的。