请考虑以下示例:
\documentclass{article}
\makeatletter
\newcommand\fail[1]{%
\ifnum1=#1% <- here a space is missing
\expandafter \@firstoftwo
\else
\expandafter \@secondoftwo
\fi
}
\makeatother
\begin{document}
\fail{1}{true}{false}
\end{document}
当 1=1 时,会打印 false;当 1=2 时,会打印 truefalse。
我知道这是一个\ifnum
过度扩展以期找到数字的延续的问题,并且可以通过在后面插入一个空格来解决这个问题#1
。
但我不明白到底发生了什么。
这个答案似乎相关,但我仍然无法解释发生了什么。
答案1
总结
永远不要忘记常量后面的空格,除非你知道恰恰为什么省略它(这个\romannumeral
技巧是一个典型的例子,说明为什么人们可能想要省略常量后的空格)。
会发生什么?
实际情况是,由于缺少要评估的数字的终止符,因此\expandafter
被扩展;这会触及\else
,因此,根据规则,TeX 会在(请注意,已经消失)\relax
前面插入(“冻结”类型) 。参见\else
\expandafter
正常 \relax 与冻结 \relax。
因此你得到的结果与
\ifnum1=1\@firstoftwo\relax\else\expandafter\@secondoftwo\fi{true}{false}
现在扩展恢复,因为数字仍然未完成并\@firstoftwo
返回\relax
;然后测试被评估为真,因此 TeX 仍然
\relax\expandafter\@secondoftwo\fi{true}{false}
令牌\relax
被执行(或在的情况下存储\edef
)并被\else
吞噬,我们得到
《\relax》\expandafter\@secondoftwo\fi{true}{false}
现在\expandafter
删除\fi
,你最终得到
《\relax》\@secondoftwo{true}{false}
所以最后\relax false
。如果你这样做
\edef\test{\fail{1}{true}{false}}\show\test
终端确实会显示
> \test=macro:
->\relax false.
\tracingmacros=1
如果在调用之前添加\fail
,日志文件将显示
\fail #1->\ifnum 1=#1\expandafter \@firstoftwo \else \expandafter \@secondoftwo
\fi
#1<-1
\@firstoftwo #1#2->#1
#1<-\relax
#2<-\else
\@secondoftwo #1#2->#2
#1<-true
#2<-false
证实了我的分析。
用《...》
I 表示的令牌已经被发送到下一级别。
如果呼叫是\fail{2}{true}{false}
,第一步产生
\ifnum1=2\@firstoftwo\relax\else\expandafter\@secondoftwo\fi{true}{false}
由于数字尚未完成,\@firstoftwo
因此展开并返回\relax
:
\ifnum1=2\relax\expandafter\@secondoftwo\fi{true}{false}
现在测试结果可以被评估为假,因此直到\fi
(\else
不再存在)的所有内容都将被删除而不进行扩展,从而导致
\fi{true}{false}
现在\fi
消失了。事实上,\edef\test{\fail{2}{true}{false}}\show\test
印刷品
> \test=macro:
->{true}{false}.
在终端上。
\@firstoftwo
如果交换和会怎么样\@secondoftwo
?
\documentclass{article}
\makeatletter
\newcommand\fail[1]{%
\ifnum1=#1% <- here a space is missing
\expandafter \@secondoftwo
\else
\expandafter \@firstoftwo
\fi
}
\makeatother
\begin{document}
\edef\test{\fail{1}{false}{true}}\show\test
\tracingmacros=1 \tracingonline=1
\fail{1}{false}{true}
\tracingmacros=0
\end{document}
现在第一级扩展将再次添加\relax
,但我们得到
\ifnum1=1\@secondoftwo\relax\else\expandafter\@firstoftwo\fi{false}{true}
和前面一样,\@secondoftwo
展开,返回\else
。哦,但数字尚未完成,因此\relax
添加了另一个冻结,得到
\ifnum1=1\relax\else\expandafter\@firstoftwo\fi{false}{true}
现在条件被评估为真,并且测试被删除:
\relax\else\expandafter\@firstoftwo\fi{false}{true}
和前面一样,\relax
被送到胃部并\else
删除所有匹配的标记\fi
,无需扩展:
《\relax》\fi{false}{true}
消失\fi
了,我们剩下的相当于
\relax{false}{true}