宏调用之前扩展参数

宏调用之前扩展参数

我已经为此绞尽脑汁好几天了。我有一个宏,它接受一个宏名及其参数。然后它重新排列参数,然后用重新排列的参数调用传递的宏,因此结束如下:

#1{\arga}{\argb}{\argc}

处理过的参数在哪里arga ... \argc。但是。我需要\arga ... \argc完全展开存储在中的宏被调用。在玩了几天、等等#1之后,我无论如何也想不出这个。我不能使用,我真的不想使用,但可以使用。这是一个 MWE:\expandafter\noexpandetextoolsexpl3etextoolsetoolbox

\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"

相关内容