具体发生了什么

具体发生了什么

我需要根据条件给宏赋予特定的名称。

第一个例子有效,第二个例子无效。

使用\csname/ \endcsname(即示例 1) 是唯一正确的方法吗?还有其他方法可以使示例 2 正常工作吗?

\count255=0
\def\newdef{\advance\count255 by1
 \expandafter\def\csname
  \ifnum\count255=1
    foo%
  \else
    bar%
  \fi
 \endcsname
}
\newdef{hello}
\newdef{world}
\foo\ \bar
\bye
\count255=0
\def\newdef{\advance\count255 by1
  \ifnum\count255=1
    \def\foo
  \else
    \def\bar
  \fi
}
\newdef{hello}
\newdef{world}
\foo\ \bar
\bye

答案1

在您的第二个示例中,当\ifnum接受第一个分支时,TeX 会执行\def\foo\else...,这将带来很大问题,我们将在下面解释。接受另一个分支时,情况类似:\def\bar\fi...

具体发生了什么

\ifnum让我们以测试为真的情况为例。在这种情况下,\foo会得到定义,但不是您所期望的方式。实际定义是:

\def\foo\else\def\bar\fi{hello}

这是一个有效的宏定义,其中参数文本\else\def\bar\fi,并且替换文本hello。因此,每当\foo在该定义之后展开 时,TeX 都希望看到\else\def\bar\fi紧随其后的标记。但是在您的示例中, 调用之后的标记\foo是控制空间\,因此您会收到错误:

./faulty.tex:11: Use of \foo doesn't match its definition.
l.11 \foo\ 
           \bar
Runaway argument?

另一种情况定义\bar为:

\def\bar\fi{world}

当在 TeX 运行结束时展开时,由于相同的原因而失败(\bye与预期的标记不同\fi)。

补救

修复该问题的简单方法是在适当的时候扩展\else\fi,以便在 TeX 执行之前\def(即,\def在所选分支的到达 TeX 的胃之前)从输入流中删除不需要的标记:

\count255=0
\def\newdef{\advance\count255 by1
  \ifnum\count255=1
    \expandafter\def\expandafter\foo
  \else
    \expandafter\def\expandafter\bar
  \fi
}
\newdef{hello}
\newdef{world}
\foo\ \bar
\bye

这本质上和经典的使用 LaTeX\@firstoftwo\@secondoftwo

\else当条件为真时,扩展将删除\else和匹配之间的所有标记\fi(包括两者;请注意,如果条件仍未确定,则冻结\relax将被插入)。扩展\fi只会将其删除。因此,在第一种情况下,输入流中剩下的是:

\def\foo{hello}
\newdef{world}
\foo\ \bar
\bye

第二种情况:

\def\bar{world}
\foo\ \bar
\bye

更进一步

我上面说过,如果你\else在条件仍未确定时尝试扩展标记,\relax则会插入冻结。在这种情况下,要实现这一点只需要在 后添加一个百分号\ifnum\count255=1,因为 TeX 会继续扩展 之后的标记1(符号后面的 〈number〉=尚未完成),因此会扩展两个\expandafters这个 〈number〉 已经被完全读取了。因此,删除这里不重要的代码的末尾部分,测试代码可以是:

\count255=0
\def\newdef{\advance\count255 by1
  \ifnum\count255=1% bug here: the second <number> isn't finished!
    \expandafter\def\expandafter\foo
  \else
    \expandafter\def\expandafter\bar
  \fi
}
\newdef{hello}\show\foo
\bye

其中显示了插入的 freeze \relaxinside\foo的定义:

> \foo=macro:
\relax \else \expandafter \def \expandafter \bar \fi ->hello.
l.9 \newdef{hello}\show\foo

答案2

问题在于,正如 Frougon 的出色回答中所述,TeX 中的条件句工作方式相当奇特:当测试结果为真时,\else...\fi只会因扩展 而消失\else,这会删除匹配之前的所有内容\fi(不进行扩展)。当测试结果为假时,\else(或\fi)之前的所有内容都将被删除(不进行扩展)。悬空\fi无论如何都会消失,因为它的扩展为空。

该方法\csname有效,因为进行了扩展一路直到只剩下字符标记(其他不可扩展的标记将会引发错误Missing \endcsname),所以\else\fi将会如上所述消失。

解决方法?由于您正在进行定义,因此可扩展性不是问题,您可以使用 Knuth 首选的带有尾部递归和的构造\next

\count255=0
\def\newdef{\advance\count255 by1
  \ifnum\count255=1
    \def\next{\def\foo}%
  \else
    \def\next{\def\bar}%
  \fi
  \next
}
\newdef{hello}
\newdef{world}
\foo\ \bar

\bye

相关内容