\let\foo\bar 与 \let\foo=\bar (带等号的 let)

\let\foo\bar 与 \let\foo=\bar (带等号的 let)

两种使用\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

=除了当你想让\lettoken 与以下内容相同这种显而易见的情况之外,还有一种情况是强制性的=

\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=

相关内容