\let 和 \edef 之间有什么区别?

\let 和 \edef 之间有什么区别?

当你写作时

\edef\a{\b}
\let\a\b

结果通常相同。这两个命令有什么区别?它们什么时候表现相同?什么时候表现不同?

(这个问题不是为我而问的;灵感来自这个问题比较\let\def,我问这个问题是为了将来可以作为参考,指导人们。)

答案1

\edef扩展了参数,而\let没有。下面是一个例子来说明区别:

\def\txt{a}
\def\foo{\txt}
\let\bar\foo
\bar -> a
\def\txt{b}
\bar -> b

然而,

\def\txt{a}
\def\foo{\txt}
\edef\bar{\foo}
\bar -> a
\def\txt{b}
\bar -> a

还有其他不同,比如参数等等。但如何扩展可能是最重要的(?)。

这是一个有趣的问题。我可以进一步扩展这个问题吗?

\let和之间有什么区别\expandafter\def\expandafter\foo\expandafter?它们的行为总是一样的吗?

\def\txt{bar}
\def\foo{\txt}
\expandafter\def\expandafter\bar\expandafter{\foo}
\let\BAR\foo

{\tt \string\bar = \meaning\bar}\par
{\tt \string\BAR = \meaning\bar}

对这个反问句的隐晦回答,因为最初的问题是我写的:)

如果您对不带参数的宏执行此操作,则它们之间的差异可以忽略不计。另一方面,\expandafter\def...如果您尝试复制带参数的宏的定义,则不能使用该构造。

事实上,这揭示了我希望人们在这里讨论的一个方面。\let在执行宏的瞬间创建宏的文字副本,而\edef将获取宏的内容并递归扩展它以完全创建一个新的宏。当您仅将宏用作存储数据的位置(例如\def\name{Will})时,差异基本上无关紧要,但是当将宏用作接受参数或具有对其应用了各种扩展限制的内容的“函数”(使用\protected\noexpand等等)时,差异确实非常重要。

答案2

正如已经说过的,\edef扩展了它的参数。这不仅意味着内容将不同于带有的定义\let,还意味着参数必须是可扩展的。你总是可以做\let\a\b但例如\edef\a{\tableofcontents}会中断。还\edef扩展了它的参数,它不会执行它们。所以像这样的事情不会起作用:

\def\my{\def\myinner{c}} 
\edef\test{\my}

答案3

主要区别在于,它\let创建了对现有值的新引用(即名称),而\def(和朋友)则创建了全新的值和名称对。如果您能将名称和值视为独立的东西,那么这两个原语之间的区别就相当容易理解了(在我看来)。

答案4

除了其他答案之外,让我阐明这个问题和其他问题的一个关键点答案隐含地显示。

假设\b是一个无参数的宏,其展开可能包含\c,比如:

\def\c{Alice}
\def\b{Hello \c !}

我们必须区分(一步式)扩张和递归扩展:\def\a{\b}扩展后\a给出标记列表\b,但递归的扩展\a递归的扩展\b(此处为Hello Alice!)。由于我们处理的上下文不会递归扩展其参数(与段落主体不同),因此区别很重要。

然后:

  1. \def\a{\b}使得\a的展开式等于\b,因此重新定义\b会影响 的递归展开式\a
  2. \edef\a{\b}使\a的展开等于\b递归扩展,即通常* 非宏标记列表 — 如果是这样,则重新定义不会影响的(递归)扩展\a
  3. \let\a\b使\a的展开式等于\b的展开式,因此重新定义\b不会影响\a,就像\edef例子)。但影响\b扩展的任何事情(例如,重新定义\c)仍然会产生影响\a— 与 不同\edef

*编辑:@egreg 指出这\noexpand是一个例外 — 它允许递归扩展包含未扩展的宏。详情见https://tex.stackexchange.com/a/519/1340

相关内容