当使用 定义宏时\edef
(默认情况下会尽可能地扩展定义中的每个宏),我可以通过在\noexpand
该宏之前使用 来声明该定义中的某个宏不扩展。因此\noexpand
可以看作是对扩展所有内容的默认行为声明了一个例外。
但是,有时我只想扩展很少的命令。现在,当我使用 定义宏时\def
,默认行为是不扩展定义内的任何内容(在定义点 - 当然,它可能会在新宏在某处扩展后扩展)。在使用 或任何其他定义\def
命令定义宏时,是否有命令可以在定义点手动扩展宏(完全扩展或仅扩展一步),以便通常不扩展宏(类似于在\noexpand
内声明异常\edef
)?如果有,命令是什么?它的确切效果是什么?
为了通过示例说明这个问题,我可以使用以下代码\edef
:
\documentclass{article}
\newcounter{mycount}
\edef\foo{\noexpand\textit{\arabic{mycount}}}
\stepcounter{mycount}
\begin{document}
Before: \foo
After: \arabic{mycount}
\end{document}
输出为:
前:0
之后:1
我的问题是是否存在一个命令\doexpand
(或者是否可以定义这样的命令)使得以下代码产生相同的结果:
\documentclass{article}
\newcounter{mycount}
\def\foo{\textit{\doexpand\arabic{mycount}}}
\stepcounter{mycount}
\begin{document}
Before: \foo
After: \arabic{mycount}
\end{document}
编辑:对最后一段代码进行一些修改是可以的。我的限制不是使用宏,\def
而是以任何方式定义宏,使得该定义中的宏通常不会扩展,只要它们没有以某种方式“标记”为要扩展。
答案1
当 TeX 吸收宏的替换文本时\def
它会执行不扩张。相反,当它\edef
扩张时每一个可以递归方式扩展标记,但有一些例外。
例外情况是,扩展 产生的标记\the<token register>
不会进一步扩展。 产生的标记也是如此\unexpanded
,这与使用未命名的标记寄存器非常相似。 的扩展为\noexpand
空,但它使下一个标记暂时等同于\relax
,因此不会进一步扩展。
这似乎排除了这样做的可能性\doexpand
。但是您可以使用正则表达式:将每个控制序列更改<cs>
为\noexpand<cs>
,然后更改\noexpand\doexpand\noexpand
为不更改。
以下\pedef
(部分\edef
)实现有几个限制:\doexpand
必须位于控制序列之前并且不覆盖活动字符;也不允许使用宏参数,因此这只是一个概念证明。
\documentclass{article}
\usepackage{xparse,l3regex}
\newcounter{mycount}
\setcounter{mycount}{42}
\ExplSyntaxOn
\cs_new_protected:Npn \pedef #1 #2
{
\tl_set:Nn \l_tmpa_tl { #2 }
\regex_replace_all:nnN { (\cC.) } { \c{noexpand}\1 } \l_tmpa_tl
\regex_replace_all:nnN { \c{noexpand}\c{doexpand}\c{noexpand} } { } \l_tmpa_tl
\cs_set:Npx #1 {\l_tmpa_tl}
}
\ExplSyntaxOff
\pedef\foo{\textit{\doexpand\arabic{mycount}}}
\show\foo
这输出
> \foo=\long macro:
->\textit {42}.
答案2
对于计数器的特定情况,您可以执行类似这样的操作。这个想法是创建一个宏\savebefore{<counter>}
来保存计数器的值。这可以在任何时候使用,例如,您可以在某个时候保存该值,然后稍后保存新值,覆盖现有值。\excounter<command>{<counter>}
然后使用保存的值,而不是当前值(如果可用)或当前值(如果不可用)。
来自的测试电子工具箱用于检查计数器是否存在。
梅威瑟:
\documentclass{article}
\usepackage{etoolbox}
\makeatletter
\newcommand*\savebefore[1]{%
\ifltxcounter{before@#1}{}{\newcounter{before@#1}}%
\setcounter{before@#1}{\value{#1}}%
}
\def\excounter#1#2{%
\ifltxcounter{before@#2}{%
#1{before@#2}%
}{%
\typeout{No prior value for counter saved. Using current value.}%
#1{#2}%
}%
}
\makeatother
\newcounter{mycount}
\savebefore{mycount}% save current value
\def\foo{\textit{\excounter\arabic{mycount}}}% command defined to use saved value
\stepcounter{mycount}% change value
\begin{document}
Before: \foo
After: \arabic{mycount}
\end{document}
尽管不如 通用\doexpand
,但在某些方面提供了更大的灵活性,因为保存的值不会在命令中冻结\foo
。而是\foo
始终打印最后保存的值(如果您愿意,可以称为“当前之前”的值)。
例如,坚持\foo
:
Before: \foo
After: \arabic{mycount}
\savebefore{mycount}
Before: \foo
\addtocounter{mycount}{5}
After: \arabic{mycount}
生产
或者你可以这样说
\newcommand\footoo[1]{%
Before: \excounter\arabic{#1}\par
After: \arabic{#1}\par
}
然后使用
\footoo{mycount}
生产