部分扩展命令

部分扩展命令

我的\john命令定义如下:

\def\john{\DontExpandMe}

我现在想反复改变它的定义,不断在前面添加一些额外的东西。

\foreach\i in {ape,bat,cow,dog} {
  \xdef\john{\i,\unexpanded{\john}}
  \show\john
}

我的意图是\show\john每次迭代的命令都应该导致:

\john=macro: -> \DontExpandMe.
\john=macro: -> ape,\DontExpandMe.
\john=macro: -> bat,ape,\DontExpandMe.
\john=macro: -> cow,bat,ape,\DontExpandMe.
\john=macro: -> dog,cow,bat,ape,\DontExpandMe.

也就是说,我希望\john在某种意义上“部分扩展”。但我无法做到这一点。我尝试了以下方法

  • 如果我使用\xdef,那么整个命令就会扩展,包括\DontExpandMe部分。
  • 如果我仅使用\gdef,则\i不会扩展。
  • 如果我使用\xdefwith \unexpanded{...}around \john(就像我在当前代码中所做的那样)那么我就会得到\john=macro: -> ape,\john.and\john=macro: -> bat,\john.等等。

这是我的代码。

\documentclass{article}
\usepackage{pgffor}
\begin{document}
  \def\john{\DontExpandMe}
  \show\john
  \foreach\i in {ape,bat,cow,dog} {
    \xdef\john{\i,\unexpanded{\john}}
    \show\john
  }
\end{document}

答案1

有问题的行是:

\xdef\john{\i,\unexpanded{\john}}

\i应扩展(全部/一次?)并且\john应扩展一次

\xdef用和进行部分展开\unexpanded

\unexpanded读取左花括号之前,它处于扩展模式以吞噬空格。因此它可以用来潜入\expandafter

\xdef\john{\i,\unexpanded\expandafter{\john}}

(如果没有这个技巧, \expandafter需要:

\xdef\john{\i,\expandafter\unexpanded\expandafter{\john}}

不使用 e-TeX 也可以通过使用令牌寄存器来实现相同的效果:

\toks0=\expandafter{\john}% similar trick as above to minimize the number of \expandafter
\xdef\john{\i,\the\toks0}

令牌寄存器的内容不再内部扩展\edef

\i对和进行一个扩展步骤\john

  • 与上面相同,但对于\i也是如此:

    \xdef\john{\unexpanded\expandafter{\i},\unexpanded\expandafter{\john}}
    
  • \expandafter狂欢\gdef

    \expandafter\expandafter\expandafter\gdef
    \expandafter\expandafter\expandafter\john
    \expandafter\expandafter\expandafter{%
      \expandafter\i\expandafter,\john
    }
    

    首先\john展开一次,然后\i

答案2

有很多方法可以做到这一点;最简单但风险较大的是使用\xdef

\def\john{\DontExpandMe}
\show\john
\foreach\i in {ape,bat,cow,dog} {%
  \xdef\john{\i,\unexpanded\expandafter{\john}}%
  \show\john
}

为什么风险更大?尝试使用\textbf{ape}列表中的命令来查看效果:无法存活的命令\edef将使代码严重死亡。更好的选择是\i仅扩展一次:

  \xdef\john{\unexpanded\expandafter{\i},\unexpanded\expandafter{\john}}%

使用令牌寄存器可能是一种选择:

\def\john{\DontExpandMe}
\show\john
\foreach\i in {ape,bat,cow,dog} {%
  \toks0=\expandafter{\i}%
  \toks2=\expandafter{\john}%
  \xdef\john{\the\toks0,\the\toks2}%
  \show\john
}

交付的令牌不会在或\the<token registers>中进行进一步扩展。\edef\xdef

LaTeX3语言中的宏:

\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\prependreverselist}{mm}
 {% #1 is the macro to extend; #2 is the list
  \clist_map_inline:nn { #2 }
   {
    \tl_put_left:Nn #1 { ##1 , }
   }
 }
\ExplSyntaxOff

\newcommand{\john}{\DontExpandMe}

\prependreverselist{\john}{ape,bat,cow,dog}

\show\john

答案3

在 TeX 级别,这至少可以通过几种方式实现,具体取决于我们是否假设 e-TeX 可用。经典方式(没有 e-TeX)是使用标记寄存器进行定义:

\long\def\addtoclist#1#2{%
  \begingroup
    \toks@\expandafter{#1}%
    \toks2{#2}%
    \edef#1{\the\toks2,\the\toks@}%
  \expandafter\endgroup
  \expandafter\def\expandafter#1\expandafter{#1}%
}

我一直很谨慎,并假设新材料(#2)根本不应该扩展。上面的工作方式是 TeX 仅在 内部扩展一次标记寄存器(“toks”)\edef,因此我可以确保不会发生进一步的扩展。我使用了两个临时的 toks:(按名称\toks@寻址\toks0)和\toks2(按数字寻址)。组意味着我不会影响其中已存储的任何值。

有了 e-TeX,我们不需要 group 或 toks 分配

\protected\long\def\addtoclist#1#2{%
  \edef#1{\unexpanded{#2},\unexpanded\expandafter{#1}}%
}

其中\unexpanded原始的行为类似于toks:您可以\expandafter在之前使用来扩展要分配的材料{

(在这两种情况下,您确实需要检查是否为空的初始定义,但这不是这里的关键点。)

答案4

在...的帮助下cgnieder 的评论,我已修复我的代码如下:

\documentclass{article}
\usepackage{pgffor}
\usepackage{etoolbox}
\begin{document}
  \def\john{\DontExpandMe}
  \show\john
  \foreach\i in {ape,bat,cow,dog} {
    \xdef\john{\i,\expandonce{\john}}
    \show\john
  }
\end{document}

并在cgnieder 的其他评论,我已将代码缩短如下:

\documentclass{article}
\usepackage{pgffor}
\begin{document}
  \def\john{\DontExpandMe}
  \show\john
  \foreach\i in {ape,bat,cow,dog} {
    \xdef\john{\i,\unexpanded\expandafter{\john}}
    \show\john
  }
\end{document}

相关内容