我在使用包含 ifnum 的宏时遇到了问题。使用命令 \noexpand 后,一个宏可以正常工作,但嵌套两个宏无法编译,而是会导致错误消息
额外\fi。 \fi
如下面代码所示。如何才能让嵌套宏正常工作?
\documentclass{article}
\newcommand{\IFCONDA}{\noexpand{\ifnum1>0}}
\newcommand{\IFCONDB}{\noexpand{\ifnum1=0}}
\begin{document}
\IFCONDA
hello1
\else % \IFCONDA
world1
% next three lines cause trouble (nested ifnum)
\IFCONDB
hello2
\fi % \IFCONDB
\fi % \IFCONDA
\end{document}
答案1
令牌\noexpand
什么也不做,因为{
不可扩展。因此您的代码与没有它时一样。顺便说一句,用括号括住测试并不是好的编程方式。
所以我会假装\noexpand
不存在。你得到
{\ifnum1>0}
hello1
\else
world1
\par
\IFCONDB
hello2
\fi
\fi
(换行只是为了清晰起见)。测试返回 true,因此您仍然
}
hello1
\else
world1
\par
\IFCONDB
hello2
\fi
\fi
该支架与原来的支架保持平衡,然后被移除。
现在 TeX 发送“hello1”和一个空格进行排版,并保留
\else
world1
\par
\IFCONDB
hello2
\fi
\fi
由于测试结果为真,TeX 会丢弃从\else
到 匹配的所有内容\fi
无扩展,所以\IFCONDB
被丢弃并且匹配的\fi
是第一个。
由于从未见过\fi
测试,因此您仍然会遇到与任何内容都不匹配的情况。\ifnum1=0
如果第一个测试是,情况会有所不同\ifnum1<0
,因为在这种情况下,\IFCONDB
在处理“错误文本”时会扩展,但你会得到
### simple group (level 2) entered at line 14 ({)
### simple group (level 1) entered at line 8 ({)
由于牙套不匹配。
我无法再多说,因为不清楚你的目的是什么。
可以使用条件样式,但需进行以下更改:
\documentclass{article}
\newcommand{\CONDA}{TT\fi\ifnum1>0 }
\newcommand{\CONDB}{TT\fi\ifnum1=0 }
\begin{document}
\if\CONDA
hello1
\else % \IFCONDA
world1
% next three lines cause trouble (nested ifnum)
\if\CONDB
hello2
\fi
\fi % \IFCONDA
\end{document}
值得研究它的工作原理……
答案2
您不能隐藏\if
宏中的原始标记(除非您小心隐藏匹配的\else
and \fi
),使用采用两组{}
真和假的语法要简单得多,例如\ifthenelse
:
\documentclass{article}
\usepackage{ifthen}
\newcommand{\IFCONDA}{\ifthenelse{1>0}}
\newcommand{\IFCONDB}{\ifthenelse{1=0}}
\begin{document}
\IFCONDA
{hello1}
{%
world1
% next three lines cause trouble (nested ifnum)
\IFCONDB
{hello2}
{}%
}% \IFCONDA
\end{document}
答案3
虽然您的代码永远无法工作,原因如 egreg 和 David 所述。可以使用 TeX 宏和巧妙选择的参数文本来近似地实现它(请参阅以下内容之一:TeXbook 第 20 章、TexbyTopic 11.5,或TeX 宏的工作原理:第 4 部分)。
我提供了两种可能的解决方案,不过我想说你可能不想这样做,最好ifthen
按照 David 的建议使用。首先,我展示了一个条件是静态的解决方案(例如 1>0),然后展示了一个更通用的解决方案,可以在更现实的条件下工作。
\documentclass{article}
\edef\ifconda #1\fi{\ifnum1>0 #1 \fi}
\edef\ifelseconda #1\else#2\fi{\ifnum1>0 #1 \else #2 \fi}
\edef\ifcondb #1\fi{\ifnum1=0 #1 \fi}
\edef\ifelsecondb #1\else#2\fi{\ifnum1=0 #1 \else #2 \fi}
\begin{document}
% original test case
\ifelseconda{
hello1
}\else{
world1
\ifcondb{
hello2
}\fi
}\fi
---
% more general test case
\ifelseconda{
\ifelsecondb{
hello1
}\else{
hello2
}\fi
}\else{
\ifelsecondb{
hello3
}\else{
hello4
}\fi
}\fi
\ifelseconda{
\ifelseconda{
world1
}\else{
world2
}\fi
}\else{
\ifelseconda{
world3
}\else{
world4
}\fi
}\fi
\end{document}
排版:
hello1
---
hello2
world1
请注意,这仅在定义等时可以并且应该评估条件时才会起作用\ifconda
......因为\edef
在定义时会扩展其替换文本(例如\edef\ifelseconda #1\else#2\fi{\ifnum1>0 #1 \else #2 \fi}
扩展到\edef\ifelseconda #1\else#2\fi{#1}
)。
如果您不想在定义时评估条件,则可以使用简单的\def
,但我们需要更改拼写\fi
以避免需要过多的令牌杂耍(这可能是可能的,但超出了我想要推理的范围)。
\documentclass{article}
\def\ifconda #1\fii{\ifnum1>0 #1 \fi}
\def\ifelseconda #1\else#2\fii{\ifnum1>0 #1 \else #2 \fi}
\def\ifcondb #1\fii{\ifnum1=0 #1 \fi}
\def\ifelsecondb #1\else#2\fii{\ifnum1=0 #1 \else #2 \fi}
\begin{document}
% original test case
\ifelseconda{
hello1
}\else{
world1
\ifcondb{
hello2
}\fii
}\fii
...
答案4
\if..\else..\fi
-matching 只适用于 TeX 的\if..
、\else
和\fi
-primitives。
\if..\else..\fi
-matching 不适用于名称中包含短语if
或的宏标记。IF
对于\if..\else..\fi
可扩展标记的匹配扩展,通常仅考虑在分支中表示实际情况的标记,因此这些标记不会被丢弃。
为了您的代码更清晰,我们将 重命名\IFCONDA
为\MACROA
和\IFCONDB
为\MACROB
:
\documentclass{article}
\newcommand{\MACROA}{\noexpand{\ifnum1>0}}
\newcommand{\MACROB}{\noexpand{\ifnum1=0}}
\begin{document}
\MACROA
hello1
\else
world1
% next three lines cause trouble (nested ifnum)
\MACROB
hello2
\fi
\fi
\end{document}
因此,当处理完序言后\begin{document}
,您将得到以下内容:
\MACROA
hello1
\else
world1
% next three lines cause trouble (nested ifnum)
\MACROB
hello2
\fi
\fi
现在\MACROA
已展开,并且您有如下内容:
\noexpand{\ifnum1>0}%
hello1
\else
world1
% next three lines cause trouble (nested ifnum)
\MACROB
hello2
\fi
\fi
现在\noexpand
“告诉”TeX 如果后续标记可扩展,则不要扩展该标记。但是后续标记{
不可扩展,因此\noexpand
实际上没有效果:
{\ifnum1>0}%
hello1
\else
world1
% next three lines cause trouble (nested ifnum)
\MACROB
hello2
\fi
\fi
现在由于{
TeX 打开了一个局部作用域,然后遇到了\ifnum1>0
。请注意,括号嵌套 ( {
... }
) 独立于\if..
-- \else
-nesting \fi
!\ifnum1>0
为真,因此 TeX 会处理所有内容,直到找到匹配\fi
或匹配的\else
。 (如果\fi
遇到匹配,则将其删除。如果\else
找到匹配,则该匹配以及该匹配和该匹配\else
之间的所有内容以及匹配本身都将被删除。)\else
\fi
\fi
因此 TeX 现在遇到}
并关闭了局部作用域。然后 TeX 遇到字符标记h
、e
、l
、l
、o
和1
空格标记。(空格标记的出现是因为当 .tex 输入文件中发现输入行结束时,TeX 通常将空格标记附加到标记流中,而附加到标记流中的最后一个标记不是控制字标记。1
不是控制字标记而是字符标记。)这些标记会被进一步处理,因此它们可能会影响排版/创建 .pdf 文件的结果。然后\else
找到匹配项。因此,匹配项\else
和匹配项与匹配项之间的所有内容以及\fi
匹配项\fi
本身都会被丢弃。第一个\fi
被视为匹配项\fi
。第二个\fi
被视为额外的\fi
。
为了避免在\if..
- \else
- -\fi
嵌套时产生混淆,在许多情况下你可以这样做:
\documentclass{article}
\newcommand\FirstOfTwo[2]{#1}%
\newcommand\SecondOfTwo[2]{#2}%
\newcommand\CheckWhetherConditionA{\ifnum1>0 \expandafter\FirstOfTwo\else\expandafter\SecondOfTwo\fi}%
\newcommand\CheckWhetherConditionB{\ifnum1=0 \expandafter\FirstOfTwo\else\expandafter\SecondOfTwo\fi}%
\begin{document}
\CheckWhetherConditionA{hello1 }{world1\par\CheckWhetherConditionB{hello2}{}}%
\end{document}
(我说“在许多情况下”是因为\FirstOfTwo
/ \SecondOfTwo
-方法不适用于选择\verb|...|
-命令或verbatim
-环境或类似物的两个实例之一,它依赖于这些命令/环境引入的类别代码制度的变化,这些变化在从 .tex 输入文件读取并标记其参数/内容时生效。)