\expandafter,\csname 与定义宏测试相关的问题

\expandafter,\csname 与定义宏测试相关的问题

就在我以为自己开始理解的时候\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之后的 不打算扩展。无论多少\expandafters 都不会改变这一点。

如果那不是你的本意,请将我的演讲视为针对观众的。

相关内容