是否可以定义一个“\superexpandafter{n}”来扩展为 2^n-1 个“\expandafter”?

是否可以定义一个“\superexpandafter{n}”来扩展为 2^n-1 个“\expandafter”?

这个问题的灵感来自于问题。见标题。

答案1

(抱歉,这更像是一篇文章,而不是一个答案。)

TH 声称“没有一个真正好的方法来做这件事,需要参数。”好吧,我也是这么想的,但是菲利普的回答伊安尼斯的原始问题证明我错了。我利用他的巧妙\csname想法做了一个\multexpafter以扩展数作为参数的命令。新的:我已经实现了一个更高效的版本,允许 0-100 扩展。

该命令的关键特征\multexpafter是它始终需要 3 个步骤才能完全展开。(我相信更少的步骤是不可能的,但请随意证明我错了;见上文)这与需要的连续s:-)形成对比2^n-1\expandaftern。我希望以下 TeX 代码能够工作(比较“Hello world”示例我的答案对于 Yiannis 的问题):

\def\a#1.{#1}
\def\b#1:{#1.}
\def\c#1,{#1:}
\def\d{Hello world!,}
\multexpafter4\a
\multexpafter4\b
\multexpafter1\c
\d

解释,从最后开始: 应该在 之后\multexpafter1扩展一次。这应该在 的扩展之前发生,因此前面的扩展了4 次。需要其中三个扩展步骤才能完全扩展,然后最后几个步骤将 扩展一次。前面的也是一样:它将 扩展4 次;这些步骤中的最后一步将 扩展一次。更一般地,使用\d\c\b\multexpafter4\b\multexpafter1...\multexpafter1\c\multexpafter4\a\multexpafter4\b...\b

\multexpafter{<3+n>}\a
\multexpafter{<m>}\b\c

在展开 之前,先展开\cm 次,然后展开 n 次。同样,需要 3+n 个展开步骤中的 3 个才能完全展开;然后 就消失了,因此接下来的 n 个展开步骤作用于。\b\a\multexpafter{<m>}\multexpafter{<m>}\b


这是 的新实现\multexpafter(将使用tex或进行编译pdftex)。它使用了 e-TeX \numexpr(对我来说,这有点像作弊):

\catcode`@=11
\let\expandafter@i\expandafter
\count255=1
\loop\ifnum\count255<200
    \edef\temp{\the\count255}
    \advance\count255 by 1
    \expandafter\def\csname expandafter@\romannumeral\count255\expandafter\endcsname
    \expandafter{\expandafter\expandafter
        \csname expandafter@\romannumeral\temp\endcsname \expandafter}%
\repeat
\def\expandafter@{}
\def\expandafter@e{\errmessage{Argument of \noexpand\multexpafter must be between 0 and 100}}
\def\multexpafter#1{\csname expandafter@\ifnum#1>100 e\else
    \csname expandafter@\ifnum#1=0 ii\else\romannumeral\numexpr2*#1\fi\fi\endcsname
    \endcsname\csname expandafter@\romannumeral#1\endcsname}
\catcode`@=12

\def\a#1.{#1}
\def\b#1:{#1.}
\def\c#1,{#1:}
\def\d{Hello world!,}
\multexpafter4\a
\multexpafter4\b
\multexpafter1\c
\d
\bye

简单解释一下:在循环中我构建了宏,\expandafter@ii最多\expandafter@cc可展开 200 次。的 3 个展开步骤\multexpafter{<n>}如下:第一步生成宏的替换文本。第二步\csname执行,触发

\csname expandafter@\romannumeral<n>\endcsname

扩展 2n 次,依次执行所需的 n 次扩展。第二步的结果只是控制序列\expandafter@,在第三步中将其扩展为零。


为了进一步说明,以下是方法菲利普的例子看起来像\multexpafter

\def\a{a}
\def\b{b}
\def\c{\C}\def\C{c}
\def\d{d}
\def\e{\E}\def\E{\ee}\def\ee{e}
\multexpafter3\def
\multexpafter3\temp
\multexpafter4{%
\multexpafter4\a
\multexpafter5\b
\multexpafter4\c
\multexpafter3\d
\e
}
\show\temp

为了完整起见,下面是我对 0-6 扩展的旧的低效实现:

\catcode`@=11
\def\expandafter@one{\expandafter}
\def\expandafter@two{\expandafter\expandafter\expandafter}
\def\expandafter@three{\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter}
\def\expandafter@four{\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter}
\def\expandafter@five{\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter}
\def\expandafter@six{\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter}
\def\do@nothing{}
\def\do@nothing@{\errmessage{Argument of \noexpand\multexpafter must be between 0 and 6}}
\def\m@e@a#1{\ifcase#1
\expandafter\do@nothing \or
\expandafter\expandafter@one \or
\expandafter\expandafter@two \or
\expandafter\expandafter@three \or
\expandafter\expandafter@four \or
\expandafter\expandafter@five \or
\expandafter\expandafter@six \else
\expandafter @\fi}
\def\multexpafter#1{\csname do@nothing\expandafter@six\m@e@a
    \expandafter@five#1\expandafter@four\endcsname\m@e@a#1}
\catcode`@=12

答案2

(对于这个答案太长,我深感抱歉。)

没有一个真正好的方法来做这个需要参数的事情。这有两个原因。首先,它增加了更多必须跳过的标记。例如,考虑一下\sea{3}。如果你需要跳过这个,没有简单的方法来跳过这三个标记,至少不能使用另一个\sea。即使你省略了括号,仍然有一个额外的标记。

第二个原因与实现这一点有关。考虑一下

\let\ea\expandafter
\def\sea#1{%
        \ifcase#1
        \or \ea
        \or \ea\ea\ea
        \or \ea\ea\ea\ea\ea\ea\ea
        \fi
}

这显然是首选,但实际上并不奏效;考虑一下\sea1。它会扩展到\ifcase ... \fi,然后如果\ifcase接下来扩展了,它会扩展到\ea\or \ea\ea\ea...\fi,然后如果\ea接下来是,它会跳过\or,开始扩展一系列其他\ea。为了解决这个问题,您必须\ea在定义中添加更多,使其成为下一个\or\fi。它很快就会变得完全不可行。

你可以使用

\let\ea\expandafter
\def\seai{%
        \ea
}
\def\seaii{%
        \ea\ea\ea
}
\def\seaiii{%
        \ea\ea\ea\ea\ea\ea\ea
}
\def\seaiv{%
        \ea\ea\ea\ea\ea\ea\ea\ea\ea\ea\ea\ea\ea\ea\ea
}
\def\seav{%
        \ea\ea\ea\ea\ea\ea\ea\ea\ea\ea\ea\ea\ea\ea\ea\ea
        \ea\ea\ea\ea\ea\ea\ea\ea\ea\ea\ea\ea\ea\ea\ea
}

但它的使用和遵循并不是特别容易。

\def\a{A}
\def\b{B}
\def\c{C}
\def\d{D}

\tracingall
\seai\a\b

\seaii\a\seai\b\c

\seaiii\a\seai\b\c

\seaiv\a\seaiii\b\seai\c\d

\seav\a\seaiii\b\seai\c\d
\bye

第一行先展开,\b然后。\a第二行先展开\c,,\a然后。第三\b行先展开,,,然后。第四行先展开,,,,然后。你可以通过阅读日志来验证这一点。\c\b\a\d\c\a\b\d\c\b\a

\seai这可以通过不使用而只使用来简化,\ea因为这样不需要扩展两次即可产生效果。

编辑:
玩了一会儿之后,出现了一个模式。使用适当的定义,您可以使用

\seavii\a\seav\b\seaiii\c\seai\d\e

扩展\e\d\c\b和 最后\a。使用\seavi而不是交换和\seavii的顺序。通过更改值,可以实现其他扩展顺序,但特定顺序的作用并不明显(至少对我来说不是)。\a\b

例如,猜猜看

\seavi\a\seaiv\b\seaii\c\seai\d\e

会做。

编辑2:
可以让 TeX\seaX为我们创建各种宏,而不是采用上述容易出错的手动方法。

\def\seai{\expandafter}
\count255 1
\loop\ifnum\count255<20
        \expandafter\let\expandafter\temp\csname
        sea\romannumeral\count255\endcsname
        \advance\count255 1
        \expandafter\gdef\csname sea\romannumeral\count255\expandafter
        \expandafter\expandafter\endcsname\expandafter\expandafter
        \expandafter{\expandafter\temp\temp\expandafter}%
\repeat

答案3

[再次强调,这并不是真正回答问题,而是提出了另一种选择。]

如今,我建议使用编程环境来处理复杂的扩展问题,而不是尝试\expandafter在不可读(且无法维护)的链中重复。expl3

以下是一个例子:

\exp_args:Nooo \foo \a \b \c

这里,\foo不展开(命令N中的exp_args),\a\b\c都展开一次(o字母)。但这并不是 的确切expl3使用方式;相反,可以定义foo接受五个参数,方法是先定义\foo:nnnnn,然后创建一个变体,在执行函数之前,每个参数都会展开一次:

\usepackage{expl3}
\ExplSyntaxOn
\cs_set:Nn \foo:nnnnn { ...#1...#3...#5... }
\cs_generate_variant:Nn \foo:nnnnn {ooooo}
...
   \foo:ooooo \a \b \c \d \e
...
\ExplSyntaxOff

除了o(对于“扩展一次”)之外,还有其他参数说明符,例如V(宏的值或者(注册)、c(创建 csname)、x(完整扩展)、f('romannumeral' 扩展)等等。使用该系统可以让程序员绕过传统 TeX 编程中涉及扩展的许多问题。

答案4

我们首先直接回答这个问题。然后我们给出另一种我们认为更好的方法来反转 token 的扩展。

以下代码给出了直接答案。在第一个宏中,我们制作了一个可扩展的倒数计数器,该计数器用于宏中\expand,该计数器使用双倍参数(#2#2)扩展为自身,计数器减少(\number\BSp@decnumber{#1}

\def\BSp@decnumber#1{%
    \ifcase#1\or0\or1\or2\or3\or4\or5\or6\or7\or8\or9\or10
             \or11\or12\or13\or14\or15\or16\or17\or18\fi}

\def\expandafterN#1{\romannumeral-`X\expand{#1}\expandafter}

\def\expand#1#2{\ifnum#1=\z@\putafterfi{ }\else
    \putafterfi{\expandafter\expand
              \expandafter{\number\BSp@decnumber{#1}}{#2#2}#2}\fi}

\def\putafterfi#1#2\fi{\fi#1}

在这个定义中我们使用了一个技巧强制全面扩张\expandafterN{n}经过两次展开后,得到 2^n-1 \expandafter(问题的答案)。

现在我们定义一些宏来显示扩展的顺序(另请参阅 2011 年 1 月 2 日 Hendrik Vogt 的回答)

\def\a#1.{#1}
\def\b#1:{#1.}
\def\c#1,{#1:}
\def\d{Hello world,}

\edef\aaa{\expandafterN{10}\a\expandafterN6\b\expandafterN2\c\expandafter\d\e}
\show\aaa

这在输出窗口中给出

> \aaa=macro:
->Hello world.
l.153 \show \aaa

这正是我们想要的。请注意,对于我们想要以反向顺序扩展的每个额外标记,我们需要将计数器增加 4。这似乎不是很多,但本质上,每一步的数量\expandafter都会乘以 16。

对于反转扩展顺序的另一种方法,我们使用相同的技巧,但现在是直接的。请注意,要反转 n 个标记的扩展顺序,我们只需要 4(n-1)\expandafter和 (n-1) \romannumeral

\edef\aaa{\romannumeral-`X\expandafter\expandafter\expandafter\space\expandafter\a
          \romannumeral-`X\expandafter\expandafter\expandafter\space\expandafter\b
          \romannumeral-`X\expandafter\expandafter\expandafter\space\expandafter\c
          \romannumeral-`X\expandafter\expandafter\expandafter\space\expandafter\d
          \e}%

这又给出了所需的结果。插入标记

 \romannumeral-`X\expandafter\expandafter\expandafter\space\expandafter 

可能会使 TeX 代码变得模糊,我们可以以更多(即 2n-5))为代价\expandafter来定义宏

\def\RNexpandafter{%
   \romannumeral-`X\expandafter\expandafter\expandafter\expandafter
                   \expandafter\expandafter\expandafter\space
                   \expandafter\expandafter\expandafter}%

\edef\aaa{\RNexpandafter\a\RNexpandafterRN\b\RNexpandafter\c\expandafter\d\e}

这再次给出了期望的结果。

相关内容