以下用法\ifx
可以在多个软件包以及本网站的答案中找到。这种语法到底是什么意思?有什么替代方案和缺点吗?
\newcommand{\mycommand}[2][]{%
\ifx\\#1\\
<true>
\else
<false>
\fi
% ...
}
(我其实知道答案,但认为这个问题应该存在这里。它会时不时地出现。如果它没有得到完全的回答,我将在几天后发布自己的答案。)
答案1
这是无数种检查参数是否为空的方法之一。几乎所有方法都有一些缺点:在这种情况下,参数不能是\\
。
它做什么\ifx
?它比较后面的两个 token,无需扩大它们\meaning
如果两个标记在或 的意义上相等\show
,则测试返回成功:
如果它们是字符(或
\let
字符的控制序列),它们必须代表相同的(字符代码、类别代码)对如果它们是控制序列(但不是
\let
字符),它们必须具有相同的含义;情况太多,无法全部列出。
在分析宏之前,我们应该了解一下 TeX 条件:抽象形式是
\ifZ<test><true text>\else<false text>\fi
其中\ifZ
表示原始条件之一。 可以<test>
采用多种形式,具体取决于\ifZ
(在某些情况下为空);重要的是 是<true text>
从 结尾到 的所有内容<test>
(\else
或\fi
if\else
缺失)。
那么让我们看看当我们调用时会发生什么\mycommand{abc}
,也就是说,默认参数(空)被替换为#1
:TeX 看到的标记是
\ifx\\\\<true>\else<false>\fi
当然测试是正确的,因为后面的标记\ifx
都是\\
。
当我们调用 时\mycommand[xyz]{abc}
,TeX 看到的是
\ifx\\xyz\\<true>\else<false>\fi
和 TeX 相比\\
,x
它们是不同的。但现在<true text>
是
yz\\<true>
(记住:<true text>
是从测试到的\else
)并且它将被完全忽略。
这个测试非常安全,因为它不进行扩展;但是,如果用户调用\mycommand[\\]{abc}
,它可能会失败,因此通常会使用不同的分隔符标记,这些分隔符不应潜入用户的输入(例如\uchyph
或\vfuzz
)。另一个类似的测试可能是
\if\relax\noexpand#1\relax
但需要注意的是\if
做扩展下一个标记,直到剩下两个不可扩展的标记。第一个标记是\relax
,它是不可扩展的。第二个标记将是 扩展后的结果\noexpand
,即 的第一个标记#1
(如果非空)但不可扩展;如果#1
为空,则\noexpand
应用于\relax
,再次导致 ,\relax
因为它是不可扩展的。
在两种情况下,都会保留一些 token(\\
第一种情况下是保留,\relax
第二种情况下是保留)。最安全测试是
\if\relax\detokenize{#1}\relax
因为非空的扩展#1
可以绝不给出第一个相当于的标记\relax
:即使#1
是\relax
,\detokenize{\relax}
也会给出细绳 \relax
(其中第一个标记是字符\
)。
同样,如果#1
为空,则测试\relax
与进行比较\relax
;如果#1
不为空,则将忽略 之前的所有内容\else
。当然,它要求运行带有 e-TeX 扩展的引擎:它不适用于“Knuth TeX”。
答案2
\ifx <command> #1 <command>
<code for #1 is empty>
\else
<code for #1 is _not_ empty>
\fi
<command>
也可以是
\ifx\relax#1\relax
如果#1
为空(或不存在),则为\relax=\relax
真。如果#1
不为空,则为\relax=#1
假(除#1=\relax
),以下内容\relax
无害,因为它不执行任何操作。
使用\ifx\\#1\\
LaTeX 内核对文件名的扩展名进行测试:
\def\test#1.#2\\{%
\ifx\\#2\\
<no extension #2 is empty, because \\=\\>
\else
<extension is #2>
\fi