就在我以为自己开始理解的时候\expandafter
,我遇到了这个问题,我试图测试 、 等是否\ConditionG
在\ConditionH
循环中定义。我尝试了所有能想到的组合,但无法让它工作:
\documentclass{article}
\usepackage{pgffor}
\usepackage{etoolbox}
\begin{document}
%\def\ConditionG{}% This should show up as undefined
\def\ConditionH{}
\foreach \i in {G, H}{
\expandafter\ifdefined\csname Condition\i\endcsname% Incorrect Results: everything defined
\textbackslash Condition\i\ is defined\par
\else
\textbackslash Condition\i\ is not defined\par
\fi
}
\hrule
\foreach \i in {G, H}{% Using etoolbox
\ifdef{\expandafter\csname Condition\i\endcsname}{% Incorrect Results: everything defined
\textbackslash Condition\i\ is defined\par
}{
\textbackslash Condition\i\ is not defined\par
}
}
\end{document}
约瑟夫详细解释何时使用 \edef、\noexpand 和 \expandafter?对我来说很有意义,但似乎无法将其应用于我的问题。
答案1
您应该使用\ifcsname
...\endcsname
来代替。
注意\csname
...\endcsname
使得未定义的控制序列为\relax
。参见 TeXbook 练习 7.7:
当
\csname
第一次使用来定义控制序列时,该控制序列将被视为等同于,\relax
直到被重新定义为止。
您可以像这样测试宏:
\documentclass{article}
\usepackage{pgffor}
\usepackage{etoolbox}
\begin{document}
\def\ConditionH{}
\foreach \i in {G, H}{
\ifcsname Condition\i\endcsname
\textbackslash Condition\i\ is defined\par
\else
\textbackslash Condition\i\ is not defined\par
\fi
}
\hrule
\foreach \i in {G, H}{% Using etoolbox
\ifcsdef{Condition\i}{%
\textbackslash Condition\i\ is defined\par
}{
\textbackslash Condition\i\ is not defined\par
}
}
\end{document}
答案2
此外Leo 的回答,如果没有 e-TeX,对未定义控制序列的通常测试是
\def\ifundefined#1{%
\begingroup\expandafter\expandafter\expandafter\endgroup
\expandafter\ifx\csname #1\endcsname\relax
\expandafter\@firstoftwo
\else
\expandafter\@secondoftwo
\fi
}
通过在组内执行\csname
...\endcsname
并测试我们想要的名称的含义是否等于 来实现\relax
。组意味着如果控制序列未定义,则它只会\relax
在测试中变成 ,而不是全局的。缺点是这个测试不可扩展。当然,它无法区分未定义的控制序列和故意等于 的控制序列。\relax
(\expandafter
这里的 s 使 发生\csname
在 之前\ifx
,而 本身又发生在 之前\endgroup
。)
附注:在 LaTeX2e 内核中,\@ifundefined
不使用分组方法,因此它有一个可扩展的测试。这意味着每次使用\@ifundefined
之前未定义的控制序列时,它最终都会被定义为\relax
。
答案3
除了其他更相关的答案之外,我想提一下,您的第二个代码块使用了\expandafter
冗余。 的结果\expandafter\csname Condition\i\endcsname
只是\csname Condition\i\endcsname
,因为“C”在 之前扩展\csname
。事实证明,\csname
本身继续扩展并最终得到\i
,但例如像 这样的操作\expandafter\toks0={\i}
会适得其反(它扩展了 0,而不是\i
)。
您可能打算将其用作\expandafter
“执行此”命令,就好像它的功能是:\expandafter\a\b
首先扩展\b
,然后扩展\a
。实际上,它所做的只是\b
“就地”扩展,而不触及\a
;如果\a
确实扩展,那是因为 TeX 自己的“嘴巴”会继续处理输入,并在处理过程中进行扩展。如果\a
不可扩展但可执行,则将执行它,除非 被禁止,例如\edef
。例如,\edef\macro{\def\a{a}\a}
没有定义\macro=a
,而是给出错误(假设\a
尚未定义),因为它不扩展\def
,因此不知道它\a
之后的 不打算扩展。无论多少\expandafter
s 都不会改变这一点。
如果那不是你的本意,请将我的演讲视为针对观众的。