我有一个场景,我正在测试一个组内的条件,然后触发输入相应的外部文件。问题是外部文件可能包含我以后需要的控制序列。我试图确保扩展将在关闭组后发生,如下例所示。(我省略了测试条件,因为这个例子仍然重复了我的错误。)
\begin{filecontents}{anotherfile}
\def\aemoon{This is the moon}
This is my other file.
\end{filecontents}
\documentclass{article}
\begin{document}
Testing
\begingroup
\def\helloworld{\input{anotherfile}}
\expandafter\helloworld
\endgroup
\aemoon
\end{document}
但这会导致错误
! Undefined control sequence.
l.17 \aemoon
下面的情况也一样,不需要输入外部文件
\documentclass{article}
\begin{document}
Testing
\begingroup
\def\helloworld{\newcommand\aemoon{this is the moon}}
\expandafter\helloworld
\endgroup
\aemoon
\end{document}
在任一例子中,我都期望该命令\aemoon
由于 而在组之外定义\expandafter
。(由于该组尚未定义,\aftergroup
因此显然不起作用。)\helloworld
回想一下我最初输入另一个文件的情况,我知道我可以写类似的东西
\begin{filecontents}{anotherfile}
\def\aemoon{This is the moon}
This is my other file.
\end{filecontents}
\documentclass{article}
\begin{document}
Testing
\begingroup
\gdef\helloworld{\input{anotherfile}}
\endgroup
\helloworld
\aemoon
\end{document}
所以,我确实有解决办法。但我想知道为什么前两个例子不起作用,因为我的理解是扩展应该在组外进行,但是...
那么,这里发生了什么事?
答案1
你应该说:
\begingroup
\def\helloworld{\newcommand\aemoon{this is the moon}}
\expandafter
\endgroup\helloworld
这里的想法是,在 TeX 看到 之前,\expandafter
会导致\helloworld
被扩展\endgroup
,因此首先\helloworld
被扩展,然后 TeX 看到\endgroup\newcommand\aemoon{this is the moon}
。然后它评估\endgroup
并\helloworld
超出范围,然后它评估\newcommand
并被\aemoon
定义。
您可能将这种情况与条件语句中有一个带参数的命令的情况相混淆,并且您不希望它使用\fi
或\else
和 false 子句作为其参数,因此您说:\ifsomething \expandafter\somecommand\fi
。由于\else
和是完全可扩展的,并且在扩展时会删除自身(在false 子句\fi
的情况下),这解决了该问题。\else
问题在于\expandafter\helloworld\endgroup
是\endgroup
不可扩展的,所以结果是\expandafter
扩展了\endgroup
,这根本不会改变它 --\endgroup
就像这里的 一样}
。那么 TeX 看到的就是\mycommand\endgroup
,这并不符合你的要求。
粗略地说,TeX 有两个求值阶段——扩展阶段,它接受宏并反复扩展它们;求值阶段,它接受不可扩展的基元(字符或命令,如\bgroup
、\relax
、\def
等\show
)并求值它们。\expandafter
仅与扩展阶段交互。TeX 的许多非直观行为都源于这种扩展求值区别。
但是,即使\expandafter\helloworld\egroup
的行为确实如您所期望的那样,它也是错误的,因为这与说 相同\egroup\helloworld
,这会导致错误,因为 的定义\helloworld
超出了范围。
答案2
\endgroup
不可扩展,这使得
\begingroup
\def\helloworld{\newcommand\aemoon{this is the moon}}
\expandafter\helloworld
\endgroup
相当于
\begingroup
\def\helloworld{\newcommand\aemoon{this is the moon}}
\helloworld
\endgroup
因此,\helloworld
被定义、执行,然后组中所有未定义、执行的内容\global
将不复存在。这包括\aemoon
。您可以考虑另一种选择
\begingroup
\def\helloworld{\endgroup\newcommand\aemoon{this is the moon}}
\helloworld
\helloworld
团体的尽头隐含地,\aemoon
仍保持定义。
答案3
\expandafter\helloworld\endgroup
适用\expandafter
于\endgroup
这不是错误,但它不执行任何操作,因为\endgroup
它不可扩展,所以它相当于
\helloworld\endgroup
因此, \helloworld
在团体内部采取行动是预期的行为。