我有这个 MWE,除非我在外部. (在 中定义)之前加载包,否则它不会编译(Extra \else
,)。这是我迄今为止从未违反过的一般要求,还是我在这里遇到了一些特殊情况?这似乎很普遍,因为我的第二个 MWE 以完全相同的方式失败。Extra \fi
showlabels
\if
\ifSL@AMS
showlabels.sty
\documentclass{article}
%\RequirePackage{showlabels}
\makeatletter
\newif\ifsomething
\ifsomething
\RequirePackage{showlabels}
\ifSL@AMS
\else
\fi
\else
\fi
\begin{document}
X
\end{document}
和
\documentclass{article}
\newif\ifA
\ifA
\newif\ifB
\ifB
\else
\fi
\else
\fi
\begin{document}
AB
\end{document}
答案1
Heiko 的回答有点简洁,所以我希望我可以添加一些解释,即使我没有提出替代解决方案。
我想说,你的问题的答案是“这取决于外部开关是真还是假”。我将分析你的第二个例子,该例子涉及\ifA
和\ifB
,以解释为什么 Heiko 的答案有效。
如果 TeX 发现 outer\ifA
为假,它将开始跳过标记,直到找到相应的\else
或\fi
;在执行此操作时,如果它遇到控制序列已经被定义为条件,它会跟踪它开始一个新的嵌套条件。但是当 TeX 开始跳过标记时(即,在 之后立即跳过标记时),\ifA
控制序列\ifB
还不知道是否表示条件;因此,TeX 会像跳过任何其他标记一样跳过它,而当遇到随后的 时\else
,它会认为这是它所\else
寻找的 ,即与 相匹配的\ifA
,因此它会继续执行标记。下一个标记是\fi
,它被解释为条件的结束。现在您可以看到,在 TeX\ifA
看来,以下内容显然放错了位置。\else
另一方面,如果外部\ifA
开关为真,则一切都会顺利进行,因为在这种情况下,TeX 在看到它之前就执行了它的定义\ifB
。你可以自己尝试一下:
\documentclass{article}
\newif\ifA
\Atrue
\ifA
\newif\ifB
\ifB
\else
\fi
\else
\fi
\begin{document}
AB
\end{document}
Heiko 的答案之所以有效,当然是因为 TeX 始终知道 是\ifx
条件。因此,即使它跳过 的“真实分支” \ifA
,它也能判断出第一个\else
和随后的\fi
应该与 配对,\ifx
而不是\ifA
。
添加
有一条评论建议我应该进一步澄清此事。
是的,您在考虑编程语言时可能习惯的通常范例并不适用于 TeX:TeX 是不同的,它甚至可能不应该被视为一种编程语言,而是一个配备了非常强大的宏预处理器的纯顺序处理器;然而,它有一个关键的特性:宏的含义可以在运行时定义和更改。
就我们的具体情况而言,不内置的“句法”规则规定以 开头的控制序列\if...
是条件的:这只是一个习俗应该遵守,但仅此而已。(但请注意,普通的 TeX\newif
强制执行它;LaTeX2e 不强制执行,但假设条件名称的“核心”前面有两个字母的前缀。)就 TeX 的“机器级别”而言,只有有限数量的条件:对于 Knuth 的原始 TeX3,它们是第 209-210 页列出的 14 个控制序列TeXbook,即
\ifnum
,\ifdim
,\ifodd
,\ifvmode
,\ifhmode
,\ifmmode
,\ifinner
,\if
,\ifcat
,\ifx
,\ifvoid
,\ifeof
,\iftrue
, 和\iffalse
(还有\ifcase
原语,但它与其他的完全不同)。其他排版引擎(e-TeX、pdfTeX 等)将许多其他原语条件添加到此列表中,每个原语条件都有其特定的添加列表,但只有明确属于这些有限原语列表的控制序列才能充当条件,尤其是在跳过属于尚未采用的(外部)条件分支的标记时才会被识别为条件。
那么,我们如何定义新的“自定义”条件呢?正如上面所说,TeX 中包含的宏处理器具有一个独特的特性,即宏的含义可以在运行时更改,这就是定义新条件的能力的实现方式。让我们通过一个例子来详细了解它的工作原理。
当你说
\newif\ifFOO
会发生以下事情:
首先,相当于
\let\ifFOO=\iffalse
被执行。因此,控制序列
\ifFOO
成为 的完美同义词\iffalse
;特别是,从现在起,\ifFOO
即使在高速跳过标记时,TeX 也会将其识别为条件(当然,除非或直到它被重新定义)。定义了两个新的控制序列
\FOOtrue
和\FOOfalse
,其含义与以下代码赋予它们的含义相同:\def\FOOfalse{\let\ifFOO=\iffalse} \def\FOOtrue {\let\ifFOO=\iftrue}
例如,你现在可以说
\FOOtrue
在代码的某个地方,这将使后续出现的行为与在它们的位置上所写的\ifFOO
行为完全一样;对于 也类似。\iftrue
\FOOfalse
我希望这足以阐明\if...
条件是如何起作用的。
答案2
\else
和\fi
已经定义。但如果开头\ifSL@AMS
未定义,则 TeX 将不会将未定义的控制序列识别为\if...
switch,并且\if...
、\else
和\fi
标记不再正确嵌套。
解决方法:
\makeatletter
\newif\ifsomething
\ifsomething
\RequirePackage{showlabels}
\expandafter\ifx\csname ifSL@AMS\expandafter\endcsname\csname iftrue\endcsname
\else
\fi
\else
\fi
\makeatother
这里定义了并且比较的两个命令序列,它们目前还不可见,但是当实际执行\ifx
分支时,它们将被构建。\ifx
答案3
假设\ifsomething
返回 false,则跳过“true”文本,但 TeX 仍会平衡条件。由于\ifSL@AMS
未定义,因此会跳过它,但随后\else
匹配到\ifsomething
,最终会得到“Extra \else
”。
这是使用的好地方ifthen
:
\RequirePackage{xifthen} % or ifthen
\ifthenelse{\boolean{something}}
{% true branch of \ifsomething
\RequirePackage{showlabels}%
\ifthenelse{\boolean{SL@AMS}}
{% true branch of \ifSL@AMS
% whatever
}
{% false branch of \ifSL@AMS
% whatever
}%
{% false branch of \ifsomething
% whatever
}