在定义中手动扩展宏

在定义中手动扩展宏

当使用 定义宏时\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}

生产

foo 也

相关内容