\pgfoo@inherit@methods
以下纯 TeX 代码是根据 pgf/tikz 源代码中的类似代码行(靠近中的宏的末尾<pgf/tikz installation dir>/modules/pgfmoduleoo.code.tex
)建模的:
\edef\c{%
\noexpand\let\expandafter\noexpand\csname a\endcsname%
\expandafter\noexpand\csname b\endcsname%
%}
当我打印时\c
我\meaning
得到:
macro:->\let \a \b
这不是我所期望的。我期望的是
macro:->\noexpand \let \expandafter \noexpand ... \csname b\endcsname
换句话说,我期望使用与定义 完全相同的标记列表\c
。这种期望的原因是\edef
应该只扩展宏,但 的定义中的任何标记都不\c
是宏。
为什么会\c
这样\meaning
呢?
答案1
宏,即用\def
、\edef
或定义的标记\gdef
都是\xdef
可扩展的。每个\let
可扩展标记的标记都是可扩展的。
基元可以是可扩展的;可扩展基元包括
\csname
\else
\expandafter
\fi
\if...
(任何原始条件)\input
\meaning
\noexpand
\number
\or
\romannumeral
\string
\the
e-TeX 增加了一些条件和可扩展原语
\detokenize
\eTeXrevision
\scantokens
\unexpanded
\unless
请注意,e-TeX 还添加了\protected
;用 定义的宏将不会在或 的\protected\def
一般文本中扩展。\edef
\write
、pdftex
和引擎添加了更多可xetex
扩展luatex
原语;请查看它们的文档。
在您编写的代码中,第一个\noexpand
什么都不做。实际上,的作用\noexpand
是使以下标记\relax
在下次使用时等同于(因此不可扩展);\noexpand
在完成其任务后,的扩展为空。但是,\let
一开始就不可扩展,因此在期间不会对其进行任何操作\edef
。
类似地,\expandafter
在扩展了下一个令牌之后(如果可以扩展的话),它将扩展为无。
因此你的代码\edef\c
就变成了
\let\expandafter\noexpand\csname a\endcsname\expandafter\noexpand\csname b\endcsname
(\let
相当于\relax
)
\let\noexpand\a\expandafter\noexpand\csname b\endcsname
\let\a\noexpand\b
(\a
相当于\relax
)
\let\a\b
(\b
相当于\relax
)
当\c
展开时,\let
\a
和\b
不再等同于\relax
。
答案2
\def
好的,按照要求,下面是 TeX 程序在和情况下发生的情况的概述\edef
。对于这个问题,它完全没有增加任何基本点(即\noexpand
等\expandafter
确实是“可扩展的”),但无论如何它可能对你来说很有趣。
你可能想参考TeX 程序正如您阅读下面的内容一样。
TeX 的main_control
程序(第 1030 节)的作用是读取一个标记(get_x_token
),(本质上)循环,然后决定要做什么。内循环针对水平模式下的常规字符进行了严格优化,但诸如\hrule
或 之类的东西\def
不是它的一部分(→第 1045 节),事实上\def
和\edef
是模式独立处理的一部分(→第 1210 节),并导致调用该程序prefixed_command
(第 1211 节)。此程序对许多基元/命令来说都是通用的,但对于 和\def
,\edef
它的调用方式如下:
cur_cmd
= [表示“def”的代码],并且cur_chr
= [0 为\def
, 1 为\gdef
, 2 为\edef
, 3 为\xdef
]
(正如你可能从这四个数字中猜到的那样,Knuth 随后检查“ odd(cur_chr)
”来决定当前定义是否应该是全局的,并检查“ e = (cur_chr >= 2)
”来决定是否扩展!)
无论如何,在此prefixed_command
程序(第 1211 节)中,执行落入第 1217 节,然后是第 1218 节,即:
假设您的输入有\def\foo{\bar}
或\edef\foo{\bar}
。那么在上面,get_r_token; p ← cur_cs;
将设置p
为(基本上)\foo
,然后scan_toks(true, e)
被调用。
这里, 的两个参数中scan_toks
,第一个参数(macro_def
,这里传递为true
)表示要扫描的标记列表是宏定义的标记列表(而不是\mark
、\output
、\message
、\everypar
等的标记列表),第二个布尔值(xpand
,这里传递为e
)决定是否扩展。
那么让我们深入研究scan_toks
(第 473 节)。
首先,当我们进入时macro_def
,它会进入⟨Scan and build the parameter part of the macro definition⟩
第 474 节。在我们的示例(无参数宏)中,所发生的一切就是get_token
获取标记开始组字符{
,因此这部分只是以end_match_token
添加到标记列表而结束。定义实际主体的标记列表在第 477 节中进行扫描。
这个基本上是逐个获取标记并将它们添加到正在构建的标记列表中(用于定义主体),关键的区别在于,在\edef
或的情况下\xdef
,标记会随着我们的进行而扩展:
在 的情况下,
\def\foo{\bar}
不存在扩展的复杂性,并且这部分标记列表基本上只包含标记\bar
;因此在scan_toks
返回后, 的定义\foo
被保存为包含两个代币,end_match
和\bar
。在 的情况下
\edef\foo{\bar}
,此部分(记住我们仍在 内)一次scan_toks
从输入流(当前包含 )读取一个标记,每次扩展它看到的任何标记。具体来说,此处的扩展发生在第 478 节对过程(第 366 节)的调用中。此过程的工作方式是,每次调用它时,它(本质上)只是销毁输入流中的第一个标记,并将其替换为已移除标记的“扩展”(无论什么合适的)。这样,下一次对 的调用(比如说)将拾取替换的标记。\bar
expand
get_token
如您所见,这里的“expand” / “expansion” / “expandable”基本上表示任何具有替换的东西(即应该以某种方式改变),它不仅仅意味着宏,还包括以下\noexpand
内容\expandafter
:
(不要试图直接从变量代码的名称推断可扩展的 TeX 原语;而是参见 egreg 的回答。例如,原语\number
、\romannumeral
、\string
、\meaning
、都是可扩展的,因为它们都会导致上面代码列表中的命令代码\fontname
。)\jobname
convert