MWE 宏的条件结构允许交替打印两个文本字符串“ true
”和“ false
”。因此,您可以打印类似“ false true false true false true
”的内容,而无需任何选项即可调用相同的宏六次。
但是如果打印的字符串构成了环境(即:\begin{...}
和\end{...}
),这只会在第一次起作用,就像在 MWE 中一样。第二次 \tin .... \tin
则pdflatex
表示\begin{document}
结束于\end{...}
。
\documentclass{article}
\newif\iftiny
\def\tin{\iftiny
\tinyfalse \end{tiny}
\else
\tinytrue \begin{tiny}
\fi}
\begin{document}
normal size
\tin tiny size \tin normal size
% uncomment the next line to see the problem
% \tin tiny size \tin normal size
\end{document}
\begin{tiny}
当该宏被调用四次时,第二次会发生什么?- 这段代码为什么是错误的?
- 如何修复这个问题?
答案1
每条\begin
命令都对应着发出的\begingroup
一个命令。\endgroup
\end
\tinyfalse
或等作业\tinytrue
只限于其所在组。如果您希望作业超越其所在组,则可以执行\global\tinytrue
和。\global\tinyfalse
让我们看看你的代码发生了什么。
\tin tiny size \tin normal size
变成
\iftiny\tinyfalse\end{tiny}\else\tinytrue\begin{tiny}\fi tiny size \tin normal size
由于条件最初为假,因此跳过真分支,并且
\tinytrue\begin{tiny}\fi tiny size \tin normal size
剩余。执行分配后,tiny
环境启动,\begingroup
并tiny
打印大小(\fi
由于正在工作,消失)。现在请记住,我们在一个组中;当\tin
找到时,它会展开,我们面临的是
\iftiny\tinyfalse\end{tiny}\else\tinytrue\begin{tiny}\fi normal size
现在\iftiny
遵循真正的分支,结果标记列表是
\tinyfalse\end{tiny}\else\tinytrue\begin{tiny}\fi normal size
执行赋值后,\end{tiny}
将执行其,\endgroup
这将撤消所做的赋值\tinyfalse
,因此\iftiny
仍为真。该\else...\fi
部分消失并被normal size
打印出来。
如果现在再次执行输入
\tin tiny size \tin normal size
你从返回 true 开始\iftiny
!所以
\iftiny\tinyfalse\end{tiny}\else\tinytrue\begin{tiny}\fi tiny size \tin normal size
跟随真正的分支,给出
\tinyfalse\end{tiny}\else\tinytrue\begin{tiny}\fi tiny size \tin normal size
还有一个\end{tiny}
不属于那里的东西。
答案2
除了对 egreg 的解释之外,这里还有另一种定义条件的方法:
\documentclass{article}
\let\iftiny\iffalse
\def\tinytrue{\global\let\iftiny\iftrue}
\def\tinyfalse{\global\let\iftiny\iffalse}
\def\tin{\iftiny\tinyfalse\end{tiny}\else\tinytrue\begin{tiny}\fi}
\begin{document}
normal size
\tin tiny size \tin normal size
uncomment the next line to see the problem
\tin tiny size \tin normal size
\end{document}
楼主说这个解决方案不太直观。所以我觉得\newif
有必要稍微解释一下。
该命令\newif
在 LaTeX 内核中定义如下
\def\newif#1{%
\count@\escapechar \escapechar\m@ne
\let#1\iffalse
\@if#1\iftrue
\@if#1\iffalse
\escapechar\count@}
这个非常巧妙的定义做了三件事。首先,它定义了一个等于的新命令\iffalse
。\let#1\iffalse
这等于上面的例子。
接下来的两行调用\@if
具有以下定义的宏:
\def\@if#1#2{%
\expandafter\def\csname\expandafter\@gobbletwo\string#1%
\expandafter\@gobbletwo\string#2\endcsname
{\let#1#2}}
简而言之,它吞噬输入字符串的前两个标记一和二,例如从\iftiny
您获得tiny
和从\iffalse
您获得false
。在下一步中,您将获得定义:\def\tinyfalse{\let\iftiny\iffalse}
。上面的解决方案几乎相同,但它添加了一个\global
赋值。