这个问题的灵感来自于这问题。见标题。
答案1
(抱歉,这更像是一篇文章,而不是一个答案。)
TH 声称“没有一个真正好的方法来做这件事,需要参数。”好吧,我也是这么想的,但是菲利普的回答伊安尼斯的原始问题证明我错了。我利用他的巧妙\csname
想法做了一个\multexpafter
以扩展数作为参数的命令。新的:我已经实现了一个更高效的版本,允许 0-100 扩展。
该命令的关键特征\multexpafter
是它始终需要 3 个步骤才能完全展开。(我相信更少的步骤是不可能的,但请随意证明我错了;见上文)这与需要的连续s:-)
形成对比2^n-1
\expandafter
n
。我希望以下 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
在展开 之前,先展开\c
m 次,然后展开 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}
这再次给出了期望的结果。