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