几天前,我试图向 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
它不会产生误导,因为它遵循了明确制定的规则。
当
\csname
扩展时,TeX 会寻找\endcsname
与扩展匹配的内容;然后 TeX 从它找到的字符标记(无论类别代码如何)中形成一个控制序列标记,如果发现某些非字符标记,则会发出错误。形成 token 后,TeX 会在内存中查找该 token 是否有意义。如果没有,则
\relax
通过本地赋值将该 token 设为等同。\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
无休止(并且没有溢出)。
这种行为并不奇怪,而是一个令人难以置信的事实!