我似乎不太理解宏定义\edef
与另一个定义中的宏定义的组合。最好用一个例子来说明。
以下 MWE(我知道它不是很有用,所以很少)不起作用:
\documentclass{article}
\def\mymacro#1{%
\def\do##1{##1}%
#1%
}
\begin{document}
\edef\savedValue{\mymacro{argument}}%
savedValue: \savedValue
\end{document}
错误出在行\edef
和状态中Illegal parameter number in definition of \savedValue
。
我以为\edef
会扩展\mymacro{argument}
,它首先扩展\def\do##1{##1}
为零,导致输出argument
,然后保存到\savedValue
。如果没有该行,\def\do##1{##1}
这似乎可以按解释工作,但是有了该行,我得到了上述错误。
这里有什么问题?
使用xparse
命令确实有效
\documentclass{article}
\usepackage{xparse}
\NewDocumentCommand{\mymacro}{m}{%
\def\do##1{##1}%
#1%
}
\begin{document}
\edef\savedValue{\mymacro{argument}}%
savedValue: \savedValue
\end{document}
并给出预期的输出:
使用的实际区别是什么\def
?
答案1
操作\def
不可扩展。在 中\edef
\def
只是一个保持不变的不可扩展标记。同样,\mymacro
最终被定义为,\protected\def
因此在 中不扩展\edef
。
\show\mymacro
节目
> \mymacro=\protected macro:
#1->\def \do ##1{##1}#1.
\documentclass{article}
\usepackage{xparse}
\NewDocumentCommand{\mymacro}{m}{%
\def\do##1{##1}%
#1%
}
\begin{document}
\edef\savedValue{\mymacro{argument}}%
savedValue: \savedValue
\end{document}
因此,的定义中没有可扩展的标记,\savedValue
因此在这种情况下\edef
,相当于\def
。\show\savedValue
显示
> \savedValue=macro:
->\mymacro {argument}.
然后当\savedValue
展开时它相当于
\def\do#1{#1}argument
如果你使用\def
而不是\protected\def
那么当你到达扩展\edef
mymacro
(但\def
没有所以它是一样的
\edef\savedValue{\mymacro{argument}}%
是
\edef\savedValue{\def\do##1{##1}argument}%
然后它尝试扩展\def
,但无法扩展,所以它被留下,然后它尝试扩展,所以你得到此时\do
随机定义的扩展,恰好是\do
\do=\noexpand.
事情出错了……
答案2
避免 David Carlisle 解释的问题的另一种可能性是完全避免\edef
。诀窍是将保存放在“被调用函数”内部。这有点类似于 C,其中指向分配内存的指针作为函数的第一个参数给出,通常是输入输出参数。这种模式可能可以为 LaTeX 省去很多麻烦。
\documentclass{article}
\usepackage{etoolbox}
\def\mymacro#1#2{%
\def\do##1{##1}%
\csdef{#1}{#2}%
}
\begin{document}
\mymacro{savedValue}{argument}%
savedValue: \savedValue
\end{document}
答案3
您的代码是:
\documentclass{article}
\def\mymacro#1{%
\def\do##1{##1}%
#1%
}
\begin{document}
\edef\savedValue{\mymacro{argument}}%
savedValue: \savedValue
\end{document}
有两个事实与这一解释相关:
当宏被展开时⟨定义文本⟩包含哈希序列,那么在扩展宏的结果中,这些序列中的哈希数量将减半。
使用示例的代码可得出 。所有序列中的哈希数量\mymacro{⟨argument⟩}
\def\do#1{#1}⟨argument⟩
##
⟨定义文本⟩将展开后的结果\mymacro
减半为。#
\mymacro
\def
不可扩展,因此不会从标记流中删除,但在 -expansion 期间将保持不变\edef
。(除非可扩展控制序列在\edef
-expansion 期间被扩展,其扩展导致 -token 被删除,\def
如下所示\gobble
:\def\gobble#1{} ... \edef\foo{\gobble\def whatsoever}
。)
随着\edef\savedValue{\mymacro{argument}}
可扩展标记序列的\mymacro{argument}
扩展,直到分配完成之前没有剩余的可扩展标记。
\def
不可扩展。因此,只需扩展所有可扩展标记即可\mymacro{argument}
:
\def⟨total expansion of all expandable tokens in the sequence "\do#1{#1}argument" ⟩
如果\do
被定义为不可扩展的东西,那么只需扩展所有可扩展的标记即可\mymacro{argument}
得到标记序列:
\def\do#1{#1}argument
。
\edef
在-扩展过程中产生的 token 序列很有可能是扩展的⟨定义文本⟩ 包含#1
尽管⟨参数文本⟩的定义\edef
为\savedValue
空/没有引入参数#1
。
这就是你收到错误的原因Illegal parameter number in definition of \savedValue.
如果\do
未定义,您将收到错误
! Undefined control sequence.
\mymacro #1->\def \do
发生错误之前Illegal parameter number in definition of \savedValue.
下面的例子也许能让你更好地理解它的\edef
工作原理:
\documentclass{article}
\def\mymacro#1{%
\def\do{#1}%
}
\begin{document}
\let\do=\relax
\edef\macro{\mymacro{bar}}
\show\macro
\let\foo=\relax
\def\do{\foo}%
\edef\macro{\mymacro{bar}}
\show\macro
\def\foo{\hoo}%
\let\hoo=\relax
\edef\macro{\mymacro{bar}}
\show\macro
\end{document}
控制台输出:
[...]
> \macro=macro:
->\def \do {bar}.
l.8 \show\macro
?
> \macro=macro:
->\def \foo {bar}.
l.12 \show\macro
?
> \macro=macro:
->\def \hoo {bar}.
l.16 \show\macro
?
[...]
下面的例子可能有助于更好地理解扩展过程中哈希序列的处理:
\documentclass{article}
\makeatletter
\newcommand\stringifytillrelax[1]{%
\ifx\relax#1\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
{}{\string#1\stringifytillrelax}
}%
\makeatother
\begin{document}
\def\mymacro{##}%
\message{^^JSee the amount of hashes that comes from expanding \string\mymacro: \expandafter\stringifytillrelax\mymacro\relax}%
\def\mymacro{####}%
\message{^^JSee the amount of hashes that comes from expanding \string\mymacro: \expandafter\stringifytillrelax\mymacro\relax}%
\def\mymacro{######}%
\message{^^JSee the amount of hashes that comes from expanding \string\mymacro: \expandafter\stringifytillrelax\mymacro\relax}%
\def\mymacro{########}%
\message{^^JSee the amount of hashes that comes from expanding \string\mymacro: \expandafter\stringifytillrelax\mymacro\relax}%
\edef\mymacrob{\mymacro}
\message{^^JSee the amount of hashes that comes from expanding \string\mymacrob: \expandafter\stringifytillrelax\mymacrob\relax}%
\edef\mymacroc{\mymacrob}
\message{^^JSee the amount of hashes that comes from expanding \string\mymacroc: \expandafter\stringifytillrelax\mymacroc\relax}%
\end{document}
控制台输出:
[...]
See the amount of hashes that comes from expanding \mymacro: #
See the amount of hashes that comes from expanding \mymacro: ##
See the amount of hashes that comes from expanding \mymacro: ###
See the amount of hashes that comes from expanding \mymacro: ####
See the amount of hashes that comes from expanding \mymacrob: ##
See the amount of hashes that comes from expanding \mymacroc: #
[...]