\ifcsname x\endcsname 当 \x 不明显时 \relax

\ifcsname x\endcsname 当 \x 不明显时 \relax

几天前,我试图向 Will Robertson 指出 eTeX 和 的一个缺陷\ifcsname\ifdefined但他似乎没有明白我的意思。请问,我说得对吗?以下内容提醒我们在使用\ifcsname和时要小心谨慎\ifdefined,因为它们在发出命令时返回“true” \relax?测试如下电子工具箱当目的是测试某个命令是否等同于时,应该优先使用\ifdef和。\ifcsdef\undefined

为了以后能做到这一点,\expandafter<cmd>\x我定义了

\edef\x{\unexpanded\expandafter{\csname test@\romannumeral\currentgrouplevel\endcsname}}

经过一些中间代码后,我做了

\ifcsname test@\romannumeral\currentgrouplevel\endcsname
  \def\y{Yes}
\else
  \def\y{No}
\fi
\show\y

乍一看,定义\x和后面的测试没什么关系。我首先想知道test@\romannumeral\currentgrouplevel定义在哪里。我花了一些时间才找到问题所在。

答案1

它不会产生误导,因为它遵循了明确制定的规则。

  1. \csname扩展时,TeX 会寻找\endcsname与扩展匹配的内容;然后 TeX 从它找到的字符标记(无论类别代码如何)中形成一个控制序列标记,如果发现某些非字符标记,则会发出错误。

  2. 形成 token 后,TeX 会在内存中查找该 token 是否有意义。如果没有,则\relax通过本地赋值将该 token 设为等同。

  3. \edef在将结果存储为替换文本之前,扩展在分隔替换文本的括号之间找到的标记<balanced text>

因此你的\edef\x{\csname totallyundefined\endcsname}相当于

\let\totallyundefined\relax
\def\x{\totallyundefined}

相反,\ifdefined和测试(由 e-TeX 开发人员精确选择)从不执行对未定义标记的\ifcsname分配。\relax

分配\relax是在许多其他情况则是为了防止过早扩张。TeXbook 中提到的两个案例是

\chardef\cs=10\cs
\font\cs=name\cs

其中\cs,暂时将 设为 等同于 ,\relax以便在执行赋值时停止扩展。在这种情况下,它实际上是临时的,因为\cs会立即被赋予新的含义;在 的情况下\csname则不会。问题出在哪里?如果\cs之前有一个定义,没有对 的临时赋值\relax,TeX 会\cs在第一种情况下扩展搜索数字,在第二种情况下扩展搜索文件名的结尾。更糟糕的是,如果\cs未定义,则会引发错误,因为 TeX 尚未赋予\cs新的含义。当然,不鼓励使用这样的语法。

可能不幸的是,e-TeX 使用与要检查的标记\ifcsname...\endcsname相同的规则来形成,但根本的区别在于不执行与的等效性,但事实就是如此。\csname...\endcsname\relax


就你介绍的情况而言

\edef\x{\unexpanded\expandafter{\csname test@\romannumeral\currentgrouplevel\endcsname}}

你可以间接地做到这一点:

\edef\x{\noexpand\csname @test\romannumeral\currentgrouplevel\endcsname}

这样就\csname不会被执行。当然\x需要两个扩展步骤:

\expandafter\expandafter\expandafter\cs\x

答案2

\ifcsname测试\ifdefined控制序列是否定义完全包括等于 的控制序列\relax。早期的测试使用\expandafter\ifx\csname abc\endcsname\relax的功能\csname自动将未定义的控制序列定义为等于 放松。你说得对混合这两种测试方式都很危险,因为对相同名称的任何\ifx\csname测试或其他测试都会改变对相同控制序列的其他两个测试的结果,因为它现在被定义为相等。和在内涵上都没有将控制序列定义为,实际上应该优先于,但您可能只希望它们与您的包等的控制序列一起使用。在没有其他代码使用不同测试对其进行测试的情况下定义自身。\csname\relax\ifcsname\ifdefined\relax\ifx\csname

顺便说一句:您可以使用类似分组:\begingroup\expandafter\ifx\csname abc\endcsname\relax\endgroup ..\else\endgroup .. \fi,以保持\csname定义本地化,但 LaTeX 核心宏\@ifundefined不会这样做。

答案3

不确定问题的第二部分是否会被迁移,但无论如何这里有一个解释。

\ifnum...\fi如果测试为假,则扩展为空,\relax否则,因此

   `\edef\x{\ifnum0=0\fi}`

   `macro:->\relax`

尝试一下这个有趣的测试:

    {\def\relax{0}\xdef\foo{\ifnum0=0\fi}}

这将\relax放入\foo。虽然有点棘手,但这个标记与 后面的标记不同\def,因为否则0\fi会扩展为,0\relax\fi然后00\fi,然后00\relax\fi无休止(并且没有溢出)。

这种行为并不奇怪,而是一个令人难以置信的事实!

相关内容