由于快速搜索/替换“mathcal”为“mc”,我最终得到了一个循环或递归语句,例如
\newcommand{\mc}{\mc}
这似乎导致我的文档编译挂起。这是一个已知错误吗?(为什么还没有修复?)
答案1
可以根据自身定义宏。正如评论中提到的,你需要小心。问题\newcommand\mc{\mc}
不是在定义时发生的,而是在你第一次尝试使用它时发生的,此时,如果保持原样,它将递归调用自身。有时这是你想要的行为。
但有时您想要别的东西,在这种情况下,您可以使用其中一种强大的功能\edef
来\xdef
创建您想要的东西。
这是一个非常简单的例子,它说明了如何从元素列表构建表格环境。表格环境的内容是根据先前定义的内容构建的:换句话说,它\ae@tabular@content
是根据其自身定义的,但要采取预防措施,不要扩展太多(因此使用了 中的工具)etoolbox
。
\documentclass{article}
\usepackage{etoolbox}
\usepackage{pgffor}
\makeatletter
\def\ae@tabular@content{}
\def\ae@empty@content{}
\def\aebuildtabular#1{%%
\def\ae@tabular@content{}%%
\foreach \myx/\myy in {#1}
{\ifx\ae@tabular@content\ae@empty@content
\xdef\ae@tabular@content{\expandonce\myx & \myy }%%
\else
\xdef\ae@tabular@content{\expandonce\ae@tabular@content \noexpand\\ \expandonce\myx & \myy }%%
\fi}
\begin{tabular}{lr}
\ae@tabular@content
\end{tabular}}
\makeatother
\begin{document}
\aebuildtabular{A/Apple,B/Banana,C/Carrot,D/Daikon,E/Endive,F/Fig}
\end{document}
生产
有时你可能想要递归调用修剪(或执行其他操作)。下一个示例接收一个字符串并丢弃标记,直到字符串中第一次出现E
。如果没有E
,则它仅恢复原始字符串。
\documentclass{article}
\makeatletter
\newcommand\aestriptoFirstE[1]{%%
\def\ae@old{#1}%%
#1 $\rightarrow$ \ae@striptoE#1\@nil
}
\def\ae@old{}
\def\ae@E@test{E}
\def\ae@empty@test{}
\def\ae@striptoE#1#2\@nil{%%
\def\ae@continue{}%%
\def\ae@first{#1}%%
\def\ae@second{#2}%%
\ifx\ae@E@test\ae@first
#2
\else
\ifx\ae@empty@test\ae@second
(unchanged) \ae@old
\else
\def\ae@continue{\ae@striptoE#2\@nil}%%
\fi
\fi
\ae@continue
}
\makeatother
\begin{document}
\aestriptoFirstE{ABCDEFGE}
\aestriptoFirstE{WXYZ}
\end{document}
答案2
\newcommand{\mc}{\mc}
就 TeX 基元而言,声明最终变为
\long\def\mc{\mc}
\long
与讨论的情况无关。
你必须考虑到 TeX 不会以任何方式解释宏的替换文本当它存储定义时这是必要的功能,因为当我们扩张宏,我们希望替换文本中的宏使用它们的当前的意思是,它们不是在定义时有效的,甚至不需要被定义!
举一个愚蠢的例子,考虑宏定义
\newcommand{\mybreak}{BREAK\\}
在 LaTeX 中,宏\\
在多种场合会改变其含义;它在外层表示某种含义,在\centering
或等声明\raggedright
有效时表示其他含义,而在 或 内部时则具有非常不同array
的tabular
含义tabbing
。
这是想要的!用户只需知道这\\
会导致换行。然后可以在任何可以给出的\mybreak
情况下使用,因为替换文本中的标记将在调用时被解释(扩展和执行)。\\
\\
以下是 TeXbook 中的一个例子:
\def\hex{{\count0=\n \divide\n by16
\ifnum\n>0 \hex\fi \count2=\n \multiply\count2 by-16
\advance\count0 by\count2 \hexdigit}}
\def\hexdigit{\ifnum\count0<10 \number\count0
\else\advance\count0 by-10 \advance\count0 by`A \char\count0 \fi}
标记\n
应该是一个计数寄存器。如您所见,宏\hex
有一个替换文本调用\hex
自身;但是每次调用时的值\n
都会减少,因此最终在条件分支的真分支中发生的调用\hex
将不会发生,宏的工作将有一个圆满的结局。
因此,无法实现简单的检查,即宏的替换文本不包含宏本身,因为这将使递归变得更加困难,甚至不可能。
这当然是有代价的:您的定义会导致 TeX 在发现\mc
要替换\mc
为时\mc
,将其替换为\mc
……Bingo!无限循环。程序不会挂起:它只是尽职尽责地用自身替换标记,而不会耗尽计算机中的资源;不会引发任何错误,因为从机器的角度来看,这并没有什么问题,它只是遵循规则。
另一种情况是
\newcommand{\mc}{(\mc)}
现在,在第一次调用时,\mc
TeX 的某些部分内存会被填满,在本例中是“输入堆栈”,TeX Live 2013(同时输入源)为其分配了 5000 的大小:的扩展在扩展的同时\mc
将第一个推(
入堆栈\mc
,(
在扩展的同时将第一个推入堆栈\mc
......另一个无限循环,但这个循环会填满内存。
假设\mc
最初定义为无参数宏,则在其替换文本的任一端添加括号可以通过以下方式实现
\expandafter\def\expandafter\mc\expandafter{\expandafter(\mc)}
因为\expandafter
会把手指放在替换文本里面前定义已执行。但是,在这种情况下,新的替换文本将不包含\mc
(假设原始定义中不包含)。
等效结构如下
\edef\mc{(\unexpanded\expandafter{\mc})}
但这会让我们走得太远(它需要 e-TeX,因此它不能与 Knuth 的 TeX 一起使用)。