两种使用\let
原语的方式通常都会导致相同的结果。对于每种情况都是如此吗?背后的思维过程是什么。如果它们实际上并不等同,那么是否存在只能使用其中一种的特殊情况?
附录
概括
正如您大量而又精彩的回答所表明的那样,在某些情况下,您显然必须尊重其中一种变化(例如关于对“=”的赋值和检查空格的宏)。这些都是完全可以理解的观点,它们解释了很多。现在,@Werner 的回答证实了我的一个天真的想法:
“使用 = 纯粹是为了强调正在进行分配的事实 [...]”
此外,Knuth 本人似乎有意在概要中包含“=”,尽管他也使用了另一种方式。那么他为什么要包含它呢?这可能与 @Werner 引用的 TeX 源片段的确切含义有关。说实话,我也不懂 WEB,但如果有人懂,他能否通过在答案中添加编辑来解释它(用非常简单的术语)?显然也欢迎新的答案。
边注
作为灰色区域的元素,我发现了一个例子,其中使用包含等号的语法会导致“困难”:
如果你有需要 'csname-expandafter' 机制的控制序列,并且你想坚持最低限度的利用,可以\expandafter
这样说
\expandafter\let\csname foo1\expandafter\endcsname\csname bar1\endcsname
你不能添加等号,因为它会分散 TeX 的注意力,使其无法\expandafter
正确读取 s。要使等号正常工作,你需要使用稍微不方便的版本
\expandafter\let\csname foo1\expandafter\endcsname\expandafter=\csname bar1\endcsname
答案1
=
除了当你想让\let
token 与以下内容相同这种显而易见的情况之外,还有一种情况是强制性的=
:
\let\equals==
是唯一正确的形式。
的语法规则\let
是
\let<control sequence><equals><one optional space><token>
只<equals>
需要解释一下:
<optional spaces> | <optional spaces>=
其中=
必须有类别代码 12(并且明确)。请注意不在赋值期间会尝试扩展\let
,因此可选空格必须明确。下面的例子不应该被认真对待,但它确实有效:
\edef\four{\space\space\space\space}
\expandafter\let\expandafter\cs\four= \relax
相当于。这无疑是深奥的\cs
。\relax
然而,有一个地方,=
和下面的可选空间是强制性的,而不是可选的。
假设我们需要一个宏来测试其参数中的第一个标记是否为空格,如果是,则执行某些操作。在下面的示例中,空格将被删除。
\documentclass{article}
\usepackage[T1]{fontenc}
\makeatletter
\def\foo#1{\afterassignment\@checkspace\let\next= #1}
\def\@checkspace{%
\if\noexpand\next\@sptoken
\typeout{Space found and removed}%
\else
\typeout{Space not found}\next
\fi}
\makeatletter
\begin{document}
|\foo{No space}|
|\foo{ A space}|
|\foo{~ isn't a space}|
\end{document}
终端输出将是
Space not found
Space found and removed
Space not found
如果只\let\next
使用 ,那么根据语法规则,空格确实会被删除,但我们无法区分是否存在空格;更糟糕的是,如果标记列表以 开头,=
我们就会遇到大麻烦。如果使用\let\next=
并且没有空格,前导空格=
会被接受,但初始空格仍会被删除,且不会有任何通知。
还有其他方法(例如使用\futurelet
)来解决同一问题。您可能喜欢使用与此类似的技巧来查看如何booktabs.sty
定义。\futurenonspacelet
=
实际上,Knuth在 中使用可选的 时非常连贯plain.tex
。所有顶级\let
指令都有它,因为这只会对指令的处理产生几微秒的影响,而在定义中=
很少使用。它出现在 的定义中
\alloc@
\newinsert
\newif
但在所有其他替换文本中它都被省略了,因为它会影响内存使用:替换文本中的每个标记都会占用空间,并且在开发 TeX 时,内存空间是一个问题。
相反,在manmac.tex
所有\let
指令中都有=
(除了,非常奇怪的是,在 的定义中\footnote
)。不同之处在于,如果在排版 TeXbook 时内存不足,manmac.tex
可以轻松编辑,而plain.tex
是“针对所有人”的,因此必须进行优化。
答案2
有一种情况你必须使用=
。你编写了一个以命令结尾的宏\let
,并且想要知道它后面是否有空格。下面我们使用一个技巧使\spacetoken
等于空格(例如,latex.ltx
使用 来实现这一点\@sptoken
)。
\def:{\let\spacetoken= }\: %
分析是\let\spacetoken=
允许一等号后面可以有空格,这是宏定义中的空格,因此执行\spacetoken
后等于空格\:
。这可用于测试宏参数后面是否有空格:
\def\X#1{\def\temp{#1}\afterassignment\Y\let\next= }
\def\Y{\ifx\next\spacetoken\message{Found a space after \temp!}\fi}
\X{abc} % space following the argument.
在这种情况下,\next
可以等于空格,并且 \Y 可以测试这一点。\message
上面生成的结果是“在 abc 之后发现一个空格!”。
答案3
不,这并非在所有情况下都是正确的。一个特殊情况是
\let\myequals=
\let\myrelax\relax
(很可能会导致错误:)Undefined macro \myrelax
和
\let\myequals==
\let\myrelax\relax
(做我们想做的事)。
否则,应该没有区别。我更喜欢没有 的变体=
。请注意,这不是唯一可以放置=
或 的情况。对于所有 TeX-core 样式的计数器、尺寸和胶水分配,它都是类似的。
实际上有一种情况不适用于=
,那就是当你想吃\active
东西=
时\let
:
\catcode`\=\active
\let=2
1=3
\bye
% output: 123
对阵
\catcode`\=\active
\let==2
1=3
\bye
% error: Undefined control sequence. =
% if you continue by `q`:
% output: 2 13
答案4
Knuth 本人总是使用
概要:\let<control sequence>=<token>
它的行为与维度/计数器寄存器相同:
\parindent0pt
相对\parindent=0pt
内部会测试是否存在,因此两个宏之间有或没有 的=
有效操作没有区别。\let
=