代码

代码

使用\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 #1is c、argument #2isba和argument #3is \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,它会扫描直到找到五个标记(to:)按该顺序排列 — 所有前面的标记都是 #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 会将 设置#1l,然后#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应该做的。

相关内容