添加

添加

我有这个 MWE,除非我在外部. (在 中定义)之前加载包,否则它不会编译(Extra \else,)。这是我迄今为止从未违反过的一般要求,还是我在这里遇到了一些特殊情况?这似乎很普遍,因为我的第二个 MWE 以完全相同的方式失败。Extra \fishowlabels\if\ifSL@AMSshowlabels.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

会发生以下事情:

  1. 首先,相当于

    \let\ifFOO=\iffalse
    

    被执行。因此,控制序列\ifFOO成为 的完美同义词\iffalse;特别是,从现在起, \ifFOO即使在高速跳过标记时,TeX 也会将其识别为条件(当然,除非或直到它被重新定义)。

  2. 定义了两个新的控制序列\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
  }

相关内容