我遇到了另一个扩展问题,并将其缩小到这个简单的宏测试用例,该宏确实没有什么,至少它尝试什么都不做,但并没有那么成功。
如果我将此宏声明为:
\newcommand*{\DoNothingA}[1]{#1}%
效果非常好,没有什么意外。
但是,如果这个宏定义如下:
\newcommand*{\DoNothingB}[1]{%
\def\Temp{#1}%
\Temp%
}%
然后,当尝试在 中使用它时,就会出现问题\edef
。
输出:
请注意,下面的 MWE 在最后一步产生了编译错误,因此您需要跳过<RETURN>
它才能看到输出(或者注释掉最后一个\edef
,但这样就不会显示下面的问题 2)。
下面的 MWE 得出以下结论:
最后一行尤其令人好奇,因为它产生了foo 4
,并且结果是:
\edef\NewTemp{\DoNothingA{foo 5}}
\NewTemp
\color{red}% Highlight problem output
\edef\NewTemp{\DoNothingB{foo 6}}
\NewTemp
\color{black}
~-- used \verb|\edef| to store output of macro
问题:
- 这里的扩展问题如何解释?
- 如果我们跳过编译错误,最终的输出会变成什么样
foo 4
(foo 5
会更容易接受)? - 大卫·卡莱尔曾说过“多吃一点
\expandafter
就能治愈所有已知的疾病”:-),这里定义的\expandafter
最后\edef
使用宏所需的 s序列是什么。\DoNothinB
- 我如何调整
\DoNothingB{}
宏以使用临时变量并仍然在 MWE 中的三种情况下工作(最好不必使用任何\expandafter
技巧)。
代码:
\documentclass{article}
\usepackage{xcolor}% To highlight problem output
\newcommand*{\DoNothingA}[1]{#1}% Does everything I want in all cases
\newcommand*{\DoNothingB}[1]{% Works mostly, except if used in a \edef
\def\Temp{#1}%
\Temp%
}%
\begin{document}
% These two work great.
\DoNothingA{foo 1}
\DoNothingB{foo 2}
-- used output of macro directly
% These two also work great.
\def\NewTemp{\DoNothingA{foo 3}}
\NewTemp
\def\NewTemp{\DoNothingB{foo 4}}
\NewTemp
~-- used \verb|\def| to store output of macro
\edef\NewTemp{\DoNothingA{foo 5}}
\NewTemp
\color{red}% Highlight problem output
\edef\NewTemp{\DoNothingB{foo 6}}
%\show\NewTemp% Excellent suggestion by David Carlisle yields: \def foo 4{foo 6}foo 4
\NewTemp
\color{black}
~-- used \verb|\edef| to store output of macro
% Why is the last output "foo 4" (after ignoring error)??
\end{document}
答案1
简短回答:您不能在可扩展上下文中使用诸如\def
或之类的赋值,因为它们不可扩展!\edef
\edef
长答案:
扩展\edef
其内容,直到只剩下不可扩展的标记(请注意,文本不可扩展)。\def
是一项分配,所有分配都必须执行因此不是可扩展。你的\DoNothingB
实际上做了一些事情:它定义了\Temp
!
让我解释一下以下例子中发生的情况:
\def\Temp{before}
% ...
\edef\NewTemp{\def\Temp{something}}
然后,\edef
首先尝试扩展\def
,这是不可扩展的,因此保持原样,然后\temp
扩展到before
,然后是其余部分:标记{
、s
、o
、...、g
和}
,它们都是不可扩展的。因此,您将得到,一旦扩展(即使用),它将指示 TeX 使用参数文本和替换文本进行定义\NewTemp
。\def before{something}
因为是不可变的(只要它没有被声明为活动字符),您将在此处收到错误。\NewTemp
b
efore
something
b
在您的示例中,在第四个示例中,您在之外\Temp
使用了 。在( )内部的下一个用法中,您会得到 ,因为两个用法都立即展开,这会导致上述错误。作为错误处理的一部分, 被丢弃,然后进行排版。 如果在输出之前定义不同,则输出将一致。foo 4
\DoNothingB
\edef
\edef
\edef\NewTemp{\DoNothingB{foo 6}}
\def foo4{foo 6}foo4
\NewTemp
\Temp
\def foo4{foo 6}
foo4
\Temp
这里几个\expandafter
s 没有帮助,因为顺序不是这里的问题,并且\edef
无论如何都会不断扩展所有内容,直到只剩下不可扩展的标记。
但是,您可能希望在宏\noexpand
之前添加一些直接代码,以防止它们在第一个 中过早扩展。现在这在 中不起作用。通常,您可以改用LaTeX 。根据上下文,是(外部) 还是(内部),这类似于。对于 的使用,您需要自己测试此模式,并在 内部手动添加一个。\Temp
\DoNothingB
\edef
\def
\protect
\protected@edef
\protect
\relax
\protected@edef
\@unexpandable@protect
\protected@edef
\noexpand
\def
\noexpand
edef
以下代码可实现此目的。请注意,您需要将所有 替换\edef
为\protected@edef
才能使其正常工作。正常\edef
和其他扩展上下文(如\message
或 )\write
将不起作用。
\documentclass{article}
\usepackage{xcolor}% To highlight problem output
\newcommand*{\DoNothingA}[1]{#1}% Does everything I want in all cases
\makeatletter
\newcommand*{\DoNothingB}[1]{% Works mostly, except if used in a \edef
\ifx\protect\@unexpandable@protect
\def\noexpand\Temp{#1}%
\else
\def\Temp{#1}%
\fi
\protect\Temp%
}%
\makeatother
\begin{document}
% These two work great.
\DoNothingA{foo 1}
\DoNothingB{foo 2}
-- used output of macro directly
% These two also work great.
\def\NewTemp{\DoNothingA{foo 3}}
\NewTemp
\def\NewTemp{\DoNothingB{foo 4}}
\NewTemp
~-- used \verb|\def| to store output of macro
\edef\NewTemp{\DoNothingA{foo 5}}
\NewTemp
\color{red}% Highlight problem output
\makeatletter
\protected@edef\NewTemp{\DoNothingB{foo 6}}
\makeatother
\NewTemp
\color{black}
~-- used \verb|\edef| to store output of macro
% Why is the last output "foo 4" (after ignoring error)??
\end{document}
答案2
\newcommand*{\DoNothingA}[1]{#1}
\newcommand*{\DoNothingB}[1]{\def\Temp{#1}\Temp}
\begin{document}
\DoNothingA{foo 1}
\DoNothingB{foo 2}
根据定义,这将打印“foo 1”和“foo 2”,并定义\Temp
扩展为foo 2
\def\NewTemp{\DoNothingA{foo 3}}
\NewTemp
这定义\NewTemp
为扩展为\DoNothingA{foo 3}
,因此\NewTemp
打印“foo 3”
\def\NewTemp{\DoNothingB{foo 4}}
\NewTemp
这确实
\DoNothingB{foo 4}
定义\Temp
扩展为foo 4
并打印“foo 4”
\edef\NewTemp{\DoNothingA{foo 5}}
\NewTemp
让我们慢慢来。\edef
导致 的扩展\DoNothingA
,找到它的参数并被替换文本替换; 最终等同于说
\def\NewTemp{foo 5}
现在乐趣开始了。
\edef\NewTemp{\DoNothingB{foo 6}}
这里\DoNothingB
找到它的参数并替换为
\def\Temp{foo 6}\Temp
但\edef
不满足,并\Temp
用其替换文本替换了两个实例,替换文本仍然是,foo 4
因此 TeX 执行了相当于
\def\NewTemp{\def foo4{foo 6}foo 4}
现在打电话
\NewTemp
会尝试做
\def foo4{foo 6}foo 4
结果是一个错误消息:由于没有控制序列跟随\def
,TeX 插入\inaccessible
并执行
\def\inaccessible foo4{foo 6}foo 4
定义控制序列后,结果将打印“foo 4” \inaccessible
。
所以这就是 1 和 2 的答案。3 和 4 的答案很简单:您不能在 中对\edef
任意数量的\expandafter
s 执行赋值,因为 TeX 在扩展期间不执行赋值,而只在处理的后期执行。总之:您不能在执行 时更改控制序列的含义\edef
。
解决方法
如果你想给\DoNothingB
一个容器喂食,\edef
你必须决定它在这种情况下应该做什么。如果你只是希望它在容器内部不做任何特别的事情,\def
那么
\protected\def\Temp{} % initialization
\newcommand*\DoNothingB[1]{\protected\def\Temp{#1}\Temp}
可能会做;如果你说
\edef\NewTemp{\DoNothingB{foo}}
这将扩展\DoNothingB
为令牌列表
\protected\def\Temp{foo}\Temp
但现在所有 token 都是不可扩展的,所以\edef
相当于
\def\NewTemp{\protected\def\Temp{foo}\Temp}
如果\def\NewTemp{\DoNothingB{foo}}
调用,\NewTemp
则在第一个扩展步骤之后执行与之前相同的操作。请注意,必须将其初始化\Temp
为\protected
宏,否则会出现类似
\edef\NewTemp{\DoNothingB{foo}}
会导致出现“未定义的控制序列”的错误消息。
当然,这是假设的前提是参数是\DoNothingB
由不可扩展的标记组成的,因为它们不会受到扩展保护。