在回答练习 11.5 时有这样的评论:
\def\\{\if\space\next\ % assume that \next is unexpandable
这个注释是什么意思?如果\next
可以扩展,我们必须使用以下内容吗?
\def\\{\expandafter\ifx\space\next\ %
有人能想出一个例子来说明这种差异吗?
\endlist
另外,如果我们改为如下形式,结果也不会改变\end
:
-\def\dodolist{\ifx\next\endlist \let\next\relax
+\def\dodolist{\ifx\next\end \let\next\relax
-\def\endlist{\endlist}
+%\def\end{\endlist}
-\def\demobox#1{\setbox0=\hbox{\dolist#1\endlist}%
+\def\demobox#1{\setbox0=\hbox{\dolist#1\end}%
使用的意义是什么\endlist
?
编辑
我认为定义一个控制序列(不需要定义)的方法
\endlist
就是在这里定义,当用户为新的控制序列选择一个名称时,他可以判断某个特定名称是否正在使用。
这是完整的例子:
\def\dolist{\afterassignment\dodolist\let\next= }
\def\dodolist{\ifx\next\endlist \let\next\relax
\else \\\let\next\dolist \fi
\next}
\def\endlist{\endlist}
\def\hidehrule#1#2{\kern-#1%
\hrule height#1 depth#2 \kern-#2 }
\def\hidevrule#1#2{\kern-#1{\dimen0=#1
\advance\dimen0 by#2\vrule width\dimen0}\kern-#2 }
\def\makeblankbox#1#2{\hbox{\lower\dp0\vbox{\hidehrule{#1}{#2}%
\kern-#1 % overlap the rules at the corners
\hbox to \wd0{\hidevrule{#1}{#2}%
\raise\ht0\vbox to #1{}% set the vrule height
\lower\dp0\vtop to #1{}% set the vrule depth
\hfil\hidevrule{#2}{#1}}%
\kern-#1\hidehrule{#2}{#1}}}}
\def\maketypebox{\makeblankbox{0pt}{1pt}}
\def\makelightbox{\makeblankbox{.2pt}{.2pt}}
\def\\{\if\space\next\ % assume that \next is unexpandable
\else \setbox0=\hbox{\next}\maketypebox\fi}
\def\demobox#1{\setbox0=\hbox{\dolist#1\endlist}%
\leavevmode\copy0\kern-\wd0\makelightbox}
\demobox{Tough exercise.}
\bye
答案1
对于那些找不到此行的读者:
\def\\{\if\space\next\ % assume that \next is unexpandable
在他们的 TeXbook 中,勘误表由 Donald E. Knuth 发布(“第 A311 页”)。
假设你这样做:
\def\foobar{cat}
\noindent
\demobox{The \foobar\ in the hat}
\next
获取令牌后\foobar
,\\
将扩展为相当于以下内容:
\if\space\next\ %
\else \setbox0=\hbox{\next}\maketypebox\fi
等于。根据(TeXbook p. 209) 的文档,TeX 将扩展 后面的标记,直到找到两个不可扩展的标记。一步扩展为显式空格标记,因此 TeX 继续(与此时含义相同\next
)\let
,因为它需要一个不可扩展的标记。扩展后,输入相当于:\foobar
\if
\if
\space
\next
\foobar
\next
\if〈space token〉cat\ %
\else \setbox0=\hbox{\next}\maketypebox\fi
其中 〈space token〉 表示显式空间标记(可以定义一个\let
与显式空间标记相等的控制序列,并使用它来代替 〈space token〉,参见下面的脚注 1)。现在,TeX 在 后面有两个不可扩展标记\if
:一个空间标记和一个c
字符标记(在正常 catcode 机制下属于第 11 类)。因此, 的结果\if
可以确定:它为假,因为 〈space token〉 和 的字符代码c
不同,所以 TeX 将跳到该\else
子句。
没有大的到目前为止的问题,尽管我们要cat
一次性将整个字符装箱,而不是分别装箱每个字符(c
、a
和t
);但让我们稍微回顾一下。如果我们使用:
\def\foobar{ cat}
输入将相当于:
\if〈space token〉〈space token〉cat\ %
\else \setbox0=\hbox{\next}\maketypebox\fi
测试结果将为真,TeX 将留cat\
在输入流中,这是完全错误的,因为我们应该测试我们刚才抓取的是\next
,不要插入新文本!
\next
因此,在我看来,“假设无法扩展”这一评论可以更笼统地改写为“假设\next
最终扩展为 (1) 恰好一个字符标记或 (2) 恰好一个\chardef
标记或 (3) 等于 (1) 或 (2) 的控制序列标记\let
” 2(由于“最终”,(1) 中的字符标记必然是非活动的)。实际上,您可以测试当递归扩展为单个字符标记\demobox
时是否完美运行,如下所示:\next
\def\myspacei{\myspace}
\def\myspace{\space}
\noindent
\demobox{Abc def\myspacei pU gHi}
在这里使用\myspacei
会产生与使用显式空间标记相同的结果,因为它会递归扩展为这样的标记。
下面是另一个示例,它另外使用了递归扩展为非空格字符标记的控制序列:
\def\myspacei{\myspace}
\def\myspace{\space}
\def\myxii{\myxi}
\def\myxi{\myx}
\def\myx{X}
\noindent
\demobox{Abc def\myspacei pU\myxii gHi}
您的建议:
\def\\{\expandafter\ifx\space\next\ %
...
只要 等于空格标记(显式或隐式),它也能工作\next
。\let
但它不适用于包含空格的宏形式的输入,例如\space
或我们\myspacei
上面定义的宏。事实上,\ifx
区分字符标记和宏(参见\ifx
TeXbook 第 210 页的说明)。
\endlist
最后,虽然它可以工作,但你用替换\end
听起来并不是最好的编码风格,因为\end
是现有的 TeX 原语;Knuth 选择了一些更“独特”的东西来标记要处理的文本的结尾。此外,名称\endlist
显然是选择匹配的\dolist
:这是一个一致性问题。具体参见:
\def\demobox#1{\setbox0=\hbox{\dolist#1\endlist}%
...
脚注
您可以定义一个与显式空间标记相等的控制序列,如下
\stoken
所示:\let
{\def\\{\global\let\stoken= }\\ }% now, \stoken is an implicit space token
(改编自 TeXbook 第 376 页)。TeXbook 第 336 页(练习 24.6)中给出了另外两种方法:
\def\\{\let\stoken= }\\ %
和
\def\\#1\\{}\futurelet\stoken\\ \\%
尤其是当
\next
has\let
等于非活动字符标记时,我认为 Knuth 使用“不可扩展”一词时想到的就是这种情况(实际上,非活动字符标记或 has 等于此类\let
标记的控制序列永远不会扩展)。换句话说,条件“\next
您引用的评论中的条件“不可扩展”是充分条件以确保宏正常运行,并且只是我给出的更一般条件的一个特例。:-)
答案2
它的意思是: 的参数中只允许使用不可扩展的标记。更准确地说,对应于(通过或)\demobox
的字符标记或不可扩展控制序列\chardef
\let
可打印字符(包括空格)。
如果你试试
\demobox{abc def}
\def\expandable{expandable token}
\demobox{\expandable}
你得到
这可能不是你所期望的。另一方面,像
\def\expandable{ expandable token}
会产生与预期相差甚远的结果。
因此,\demobox
宏只能在其参数由不可扩展的标记或扩展为单身的不可扩展的令牌。
还\chardef
允许使用标记以及隐式字符标记。但是,这\bgroup
也会有问题:\demobox{A \bgroup AB\egroup}
与进行比较\demobox{A AB}
,以查看问题。
您可能想\demobox
通过各种方式进行扩展,但这不是练习的目的。
关于您建议重新定义\dolist
为使用\end
而不是\endlist
:如果您愿意,您可以这样做。Knuth 不喜欢它;相反,他使用特定于\dolist
处理的控制序列。请注意,每当 扩展时, 的定义\endlist
都会产生无限循环\endlist
(可能是由于使用 的宏中的错误\dolist
),而\end
不会。