这是理解 \let \def \edef \gdef \xdef \newcommand \renewcommand \providecommand 的正确方法吗?

这是理解 \let \def \edef \gdef \xdef \newcommand \renewcommand \providecommand 的正确方法吗?

我已经查看了至少十个不同的问答,它们解释了上面列出的命令子集之间的差异。在尝试这些命令并试图理解它们之间的细微差别时,我为每个命令想出了一个简单的公式。以下是我对这些命令的看法。如果它们不正确或有更多需要了解的细节,请纠正我。

Code:定义时的行为

\let\a\b:\a指向(\b展开一次)

\def\a{\b}:\a指向\b,根本没有扩展

\edef\a{\b}\a指向(\b递归扩展,直到无法扩展)

\gdef\a{\b}\def\a{\b}:与全局相同

\xdef\a{\b}\edef\a{\b}:与全局相同

\newcommand\a{\b}:与删除相同,但如果已经存在\long\def\a{\b}则会引发错误\a\newcommand*\long

\renewcommand\a{\b}:与 相同,但如果不存在\long\def\a{\b}则会引发错误\a

\providecommand\a{\b}:与已存在相同\long\def\a{\b},但不覆盖\a

答案1

大部分正确,但有误导性。

您可以使用\let为控制序列或活动字符分配含义,并

\let\a=<token>

将赋予与执行时\a相同的含义。是可选的,其周围的单个空格标记也是可选的(仅当您要执行 时, 才是必需的)。<token>\let==\let\a==

如果你在是宏\let\a\b时这样做,那么 也将是宏;这两个宏将具有相同的第一级扩展,而且,如果稍后重新定义,也不会改变其含义。然而,更为通用,实际上可以\b\a\a\b\let<token>任何标记 (token),无论是明确的还是象征性的。

如果这样做\let\a=a,那么\ifx\a a\if\a a\ifcat\a a都将返回 true。

因此,只有当 是宏时,你的描述\let才是正确的\b,这就是我说它具有误导性的原因:控制序列可能不是宏!你可以这样做

\let\a\relax
\let\a\pi
\let\a\pageno

(我这里说的是纯 TeX)在任何情况下\a都不会是宏,而是一个不可扩展的标记。所以你会发现,当\let涉及到时,实际上不会涉及任何扩展。

\let\a\b和有什么区别\def\a{\b}? 区别很大。 第一种情况\a不需要宏,这取决于 的含义\b;第二种情况\a 一个宏,其扩展名是\b:当 TeX 进行宏扩展并发现 时\a,它将用 替换它\b并继续扩展\b(如果它是一个可扩展的标记)或执行它。

其他描述都很好,但隐藏了一个重要的事实,即\def和朋友中的替换文本可以是任何标记列表。

答案2

如果你试图完全理解\let(这个问题中指出了这一点是错误的),那么想象一下,TeX 管理着一个包含所有使用的控制序列和 catcode 活动字母的字典。这个字典中的每个项目都包含一个指向 TeX 中另一个内存结构的指针,其中意义控制序列的实现。其含义可以是:

  • TeX 原语,
  • 寄存器(类似原始寄存器或由、等\hsize给出的寄存器)\countdef\dimendef
  • 宏,
  • 字体切换器,
  • (数学)字符常量,
  • 由其 catcode/code 给出的字符标记。

当您说\let\a=\b时,TeX 会将 插入\a到控制序列的字典中(仅当它尚未在此处时),并将其指针设置为与\b字典中的指针相同的指针。如果\b不在字典中,则\a也会被删除(即未定义的控制序列)。

另一方面,\def\a{...}为给定的宏创建一个新的内存结构(即读取参数文本及其主体并保存它们)。此外,它将其放入\a控制序列的字典中(仅当它尚未存在时)并将其指针设置为新构建的内存结构,该结构中不久前保存了宏的完整含义。

\let为什么即使\b是宏,你的 的指定也是错误的:假设一个\b带参数的宏。那么 的扩展必须读取实际参数并将它们替换到宏主体中、出现的\b位置。处理时不会执行任何操作。#1#2\let

相关内容