在以下 MWE 中,我有一个宏\mytest
,用于测试其参数是否为a
或b
,分别返回“是”/“否”。此宏使用包中的(a)\ifthenelse
和 (b) 。\equal
if 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}
给出
而不受保护的\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}
答案2
命令\ifthenelse
是脆弱的。这个术语的正确定义是什么?命令是脆弱的当它在移动参数中使用时会产生不可思议的错误信息。移动参数包括:
\chapter
标题(\section
和类似的) 或 的强制参数\caption
,除非使用可选参数;- 标题或的可选参数
\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}