我已经为此绞尽脑汁好几天了。我有一个宏,它接受一个宏名及其参数。然后它重新排列参数,然后用重新排列的参数调用传递的宏,因此结束如下:
#1{\arga}{\argb}{\argc}
处理过的参数在哪里arga ... \argc
。但是。我需要\arga ... \argc
完全展开前存储在中的宏被调用。在玩了几天、等等#1
之后,我无论如何也想不出这个。我不能使用,我真的不想使用,但可以使用。这是一个 MWE:\expandafter
\noexpand
etextools
expl3
etextools
etoolbox
\documentclass{article}
\begin{document}
\def\x#1#2#3#4{%
\def\arga{#2}%
\def\argb{#3}%
\def\argc{#4}%
#1{\arga}{\argb}{\argc}}
\def\y#1#2#3{\detokenize{#1#2#3}}
\x\y{arg1}{arg2}{arg3}
\end{document}
结果是“\arga \argb \argc”,但我想要“arg1arg2arg3”。\expandafter
将在 中执行此操作\x
,但不会连续执行三个参数。etextools
有一些宏可以执行此操作,但我真的想避免它(它在某些方面与 冲突etoolbox
,我必须有)。即使我尝试过(等),etoolbox
我也无法让etextools
宏工作。\ExpandNextTwo
更新:我意识到在我的例子中,参数可以包含强大的宏,例如:
\documentclass{article}
\usepackage{etoolbox}
\begin{document}
\def\x#1#2#3#4{%
\def\arga{#2}%
\edef\argb{\ifstrequal{#3}{arg2}{arg2}{}}%
\def\argc{#4}%
{\protected@edef\z{\noexpand#1{\arga}{\argb}{\argc}}\z}}
\def\y#1#2#3{\detokenize{#1#2#3}}
\x\y{arg1}{arg2}{arg3}
\end{document}
在这种情况下(以 egreg 的答案为例),它没有完全展开。正如 Joseph 和 egreg 在下面提到的那样,这是不可行的,例如,在这种情况下,您只需要使用非健壮宏。我会让问题保持原样,因为它很有启发性。
答案1
“经典”方法是使用\expandafter
\documentclass{article}
\begin{document}
\def\x#1#2#3#4{%
\def\arga{#2}%
\def\argb{#3}%
\def\argc{#4}%
\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter#1%
\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter
{\expandafter\expandafter\expandafter\arga\expandafter\expandafter\expandafter}%
\expandafter\expandafter\expandafter{\expandafter\argb\expandafter}\expandafter
{\argc}}
\def\y#1#2#3{\detokenize{#1#2#3}}
\x\y{arg1}{arg2}{arg3}
\end{document}
我们需要很多这样的人来扩展,arg3
然后arg2
最终arg1
。(这实际上包含在expl3
中\exp_args:Nooo
)。
\expandafter
我们需要的 s数量规则是 2n– 1,其中n是我们要扩展的标记数。因此,对于前面某个标记,我们只需要\expandafter
在每个位置“跳过”一个标记,要扩展两个标记(第二个标记然后是第一个标记),我们需要三个\expandafter
标记,对于三个标记(如当前情况),我们需要七个\expandafter
标记,依此类推。如果您编写/打印出简短的第二个标记并划掉 TeX 会读取的命令,则最容易看到这一点:您会发现一切都正常。
有了 e-TeX,我们可以使用\edef
和\unexpanded
:
\documentclass{article}
\begin{document}
\def\x#1#2#3#4{%
\def\arga{#2}%
\def\argb{#3}%
\def\argc{#4}%
\begingroup
\edef\x{%
\endgroup
\noexpand#1
{\unexpanded\expandafter{\arga}}%
{\unexpanded\expandafter{\argb}}%
{\unexpanded\expandafter{\argc}}%
}%
\x
}
\def\y#1#2#3{\detokenize{#1#2#3}}
\x\y{arg1}{arg2}{arg3}
\end{document}
(您可以在没有 e-TeX 的情况下使用一系列 tok 来执行相同的操作,但这有点令人困惑,所以我通常不会这样做。)
问题说不是expl3
,但相比之下,使用它提供的最少功能的方法将会读到
\documentclass{article}
\usepackage{expl3}
\begin{document}
\ExplSyntaxOn
\def\x#1#2#3#4{
\def\arga{#2}
\def\argb{#3}
\def\argc{#4}
\exp_args:Nooo#1\arga\argb\argc
}
\ExplSyntaxOff
\def\y#1#2#3{\detokenize{#1#2#3}}
\x\y{arg1}{arg2}{arg3}
\end{document}
我希望这样可读性会好得多。(我可能想使用,\exp_args:NVVV
因为我们正在使用“存储在变量中的值”,但该函数不是预定义的,所以我在这里避免使用它。)
答案2
如果你真的希望论点充分展开,然后
\documentclass{article}
\begin{document}
\def\x#1#2#3#4{%
\begingroup\edef\z{\endgroup\noexpand#1{#2}{#3}{#4}}\z
}
\def\y#1#2#3{\detokenize{#1#2#3}}
\def\foo{This is foo}
\texttt{\x\y{arg1}{arg2}{arg3\foo}}
\end{document}
将导致打印
arg1arg2arg3This is foo
而\expandafter
基于的解决方案将打印
arg1arg2arg\foo
答案3
和functional
包中,棘手的参数扩展可以被直观的函数组合所取代,这与其他编程语言(如)类似Lua
。
复合函数的求值是从内到外的。并且\Expand
是预定义函数。
\documentclass{article}
\usepackage{functional}
\begin{document}
\PrgNewFunction \x {Nmmm} {%
#1{\Expand{#2}}{\Expand{#3}}{\Expand{#4}}%
}
\PrgNewFunction \xx {Nmmm} {%
#1{\Expand{#2}}{\Expand{\StrIfEqTF{#3}{arg2}{\Result{arg2}}{}}}{\Expand{#4}}%
}
\PrgNewFunction \y {mmm} {\detokenize{#1,#2,#3}}
\x\y{arg1}{arg2}{arg3}
\xx\y{arg1}{arg2}{arg3}
\end{document}
答案4
您可以创建一个通用宏来执行此操作:
% Macro `\expandargumentsof\macroname{arg1}{arg2}...\stopexpansion`
% *****************************************************************************
%
% Call a macro after expanding its arguments
%
\long\gdef\expandargumentsof#1#2\stopexpansion{%
\edef\tmp{\noexpand#1#2}\tmp%
}
句法:
\expandargumentsof\macroname{arg1}{arg2}...\stopexpansion
例如,在不扩展参数的情况下,以下 LaTeX 稿件将打印“3—3—3”:
\newcommand{\mymacro}[2]{%
\expandafter\def\csname #1\endcsname{#2}%
}
\newcounter{mycounter}
\stepcounter{mycounter}%
\mymacro{first}{\themycounter}
\stepcounter{mycounter}%
\mymacro{second}{\themycounter}
\stepcounter{mycounter}%
\mymacro{third}{\themycounter}
\first---\second---\third % Prints "3—3—3"
如果我们使用上面的通用宏,我们会得到“1—2—3”:
\newcommand{\mymacro}[2]{%
\expandafter\def\csname #1\endcsname{#2}%
}
\newcounter{mycounter}
\stepcounter{mycounter}%
\expandargumentsof\mymacro{first}{\themycounter}\stopexpansion
\stepcounter{mycounter}%
\expandargumentsof\mymacro{second}{\themycounter}\stopexpansion
\stepcounter{mycounter}%
\expandargumentsof\mymacro{third}{\themycounter}\stopexpansion
\first---\second---\third % Prints "1—2—3"