调用 ifthen 的 \equal 的宏在节标题中不起作用。为什么?

调用 ifthen 的 \equal 的宏在节标题中不起作用。为什么?

在以下 MWE 中,我有一个宏\mytest,用于测试其参数是否为ab,分别返回“是”/“否”。此宏使用包中的(a)\ifthenelse和 (b) 。\equalif then

运行良好,除了……

当我在部分标题中拨打电话时,我得到:

未定义的控制序列。 \equal {a}{a} l.9 ...tion{麻烦的构造:\mytest{a}.}

\documentclass{article}
\usepackage{ifthen}
\newcommand{\mytest}[1]{%
    \ifthenelse{\equal{#1}{a}}{yes}{%
    \ifthenelse{\equal{#1}{b}}{no}{%
}}}
\begin{document}
Troublesome construction: \mytest{a}.
%\section{Troublesome construction: \mytest{a}.}
\end{document}

我已注释掉有问题的部分\section{Troublesome construction: \mytest{a}.}以便它能够编译。

为什么这在章节标题中不起作用?

答案1

正如评论中所讨论的,问题在于不可扩展,因此在(或)\ifthenelse中扩展时不会产生预期的结果。这使得整个宏\edef\write\mytest脆弱,这意味着它不能用于移动参数,如节标题或标题。更多背景信息可以在脆弱命令和坚固命令之间有什么区别?

一个解决方案是\protect首先将宏或定义成健壮的。参见节/段落标题中的 xstring 函数。这在很多情况下应该有效,但对于不仅依赖于输入还依赖于某些全局“变量”的宏来说,这可能是一个问题。例如

\documentclass{article}
\newcounter{myfoo}
\begin{document}
\tableofcontents
\stepcounter{myfoo}
\section{Foo \protect\themyfoo}
\end{document}

给出

在目录中:1 Foo 0//在标题中 1 Foo 1

而不受保护的\section{Foo \themyfoo}会做正确的事情(因为\themyfoo它是可扩展的)并且在两种情况下都会显示“Foo 1”。

在这种情况下,不受保护的(可扩展的)宏会立即被“评估”,并且其扩展将用于所有后续步骤。如果宏受到保护,则不会立即扩展,而是以宏形式传递,直到真正需要评估以进行排版。但两次评估的上下文(一次在目录中,另一次在标题中)不同,结果也不同。

因此,如果您的宏不仅仅依赖其输入来确定其输出,那么您最好尝试创建一个可扩展的版本。ifthen您可以使用 的条件来代替etoolbox的条件\ifstrequal

\documentclass{article}
\usepackage{etoolbox}
\newcommand{\mytest}[1]{%
  \ifstrequal{#1}{a}
    {yes}
    {\ifstrequal{#1}{b}
       {no}
       {}}}

\begin{document}
\tableofcontents
\section{Nothing problematic here}
Troublesome construction: \mytest{a}.
\section{Troublesome construction: \mytest{a}.}
\end{document}

目录:1 这里没有问题//2 构造麻烦:是的。////章节标题:1 这里没有问题////正文:构造麻烦:是的。////章节标题:2 构造麻烦:是的。

答案2

命令\ifthenelse脆弱的。这个术语的正确定义是什么?命令是脆弱的当它在移动参数中使用时会产生不可思议的错误信息。移动参数包括:

  1. \chapter标题(\section和类似的) 或 的强制参数\caption,除非使用可选参数;
  2. 标题或的可选参数\caption(如果使用)。

处理移动参数中的脆弱命令的标准方法是在其前面加上\protect

可以创建用户定义的命令强壮的通过用 来定义它\DeclareRobustCommand

\documentclass{article}
\usepackage{ifthen}

\newcommand{\mytest}[1]{%
    \ifthenelse{\equal{#1}{a}}{yes}{%
    \ifthenelse{\equal{#1}{b}}{no}{%
}}}
\DeclareRobustCommand{\myrobusttest}[1]{%
    \ifthenelse{\equal{#1}{a}}{yes}{%
    \ifthenelse{\equal{#1}{b}}{no}{%
}}}

\begin{document}

Troublesome construction: \mytest{a} and \mytest{b}.

Troublesome construction: \myrobusttest{a} and \myrobusttest{b}.

\section{Troublesome construction: \protect\mytest{a} and \protect\mytest{b}.}

\section{Troublesome construction: \myrobusttest{a} and \myrobusttest{b}.}

\end{document}

对于hyperref,您可以改用\protected\def

\documentclass{article}
\usepackage{ifthen}
\usepackage{hyperref}

\protected\def\mytest#1{%
    \ifthenelse{\equal{#1}{a}}{yes}{%
    \ifthenelse{\equal{#1}{b}}{no}{%
}}}

\begin{document}

Troublesome construction: \mytest{a} and \mytest{b}.

\section{Troublesome construction: \mytest{a} and \mytest{b}.}

\end{document}

如果预计的参数\mytest是一个字符串(没有特殊字符或命令),则以下方法也适用于hyperref

\documentclass{article}
\usepackage{xparse}
\usepackage{hyperref}

\ExplSyntaxOn
\NewExpandableDocumentCommand{\mytest}{m}
 {
  \str_case:nn { #1 }
   {
    {a}{yes}
    {b}{no}
   }
 }
\ExplSyntaxOff

\begin{document}

Troublesome construction: \mytest{a} and \mytest{b}.

\section{Troublesome construction: \mytest{a} and \mytest{b}.}

\end{document}

hyperref这样做的好处是,您根本不会收到任何警告Token not allowed,而且案件列表很容易扩展到三个或更多。

通过进一步的抽象层次,您可以根据需要定义任意数量的类似测试。

\documentclass{article}
\usepackage{xparse}
\usepackage{hyperref}

\ExplSyntaxOn
\NewDocumentCommand{\definetest}{mm}
 {
  \NewExpandableDocumentCommand{#1}{m}
   {
    \str_case:nn { ##1 } { #2 }
   }
 }
\ExplSyntaxOff

\definetest{\mytest}
 {
  {a}{yes}
  {b}{no}
 }

\begin{document}

Troublesome construction: \mytest{a} and \mytest{b}.

\section{Troublesome construction: \mytest{a} and \mytest{b}.}

\end{document}

答案3

简单来说\ifx

\documentclass{article}
\newcommand\mytest[1]{%
    \ifx#1a\relax yes\else\ifx#1b\relax no\fi\fi}
\begin{document}
Troublesome construction: \mytest{a}.
\section{Troublesome construction: \mytest{a}.}

Troublesome construction: \mytest{b}.
\section{Troublesome construction: \mytest{b}.}

Troublesome construction: \mytest{c}.
\section{Troublesome construction: \mytest{c}.}
\end{document}

在此处输入图片描述

如果有多个字母可供选择,但只有第一个字母对于 if 子句来说是重要的:

\newcommand\mytest[1]{\myTEST#1!}
\def\myTEST#1#2!{%
    \ifx#1a yes\else\ifx#1b no\fi\fi}

如果需要测试整个单词,则使用:

\def\Bee{b}\def\Aih{a}%% Change to your "words"
\newcommand\mytest[1]{%
  \def\TEMP{#1}%
  \ifx\TEMP\Aih yes\else\ifx\TEMP\Bee no\fi\fi}

相关内容