我如何调用具有多个参数的宏的 \expandafter ?

我如何调用具有多个参数的宏的 \expandafter ?
\documentclass[pstricks,border=12pt]{standalone}
\usepackage{pst-node}
\begin{document}
\begin{pspicture}[showgrid](-5,-5)(5,5)
    \pnodes{A}(-2,3)(3,4)(2,-1)(-2,-4)(-4,0)
    \def\points{(0,0)}
    \foreach \x in {0,1,...,4}{%
            \pscircle*[linecolor=blue](A\x){3pt}%
            \xdef\points{\points+.2(A\x)}%
    }
    %The first line below causes errors.
    %\expandafter\nodexn{\points}{B}
    %\pscircle*[linecolor=red](B){3pt}
\end{pspicture}
\end{document}

我如何调用\expandafter具有多个参数的宏?

答案1

你需要做

\expandafter\nodexn\expandafter{\points}{B}

你需要“跳过”{否则

\expandafter\nodexn{\points}{B}

在扩展之前仅扩展{一次(至{) 。\nodexn

LaTeX 内核定义了一个有用的宏,\@expandtwoargs如下所示

\def\@expandtwoargs#1#2#3{%
  \edef\reserved@a{\noexpand#1{#2}{#3}}\reserved@a}

\edef是宏在“执行”之前的前两个参数。显然,你会想做类似的事情

\makeatletter
\let\expandtwoargs\@expandtwoargs
\makeatother

才能舒服地使用它。(当然,这对于那些不能安全完全扩展的参数不起作用。)

如果要扩展两个以上的参数,可以使用仅包含参数的临时宏:

\edef\tmp{{\argumentA}{\argumentB}{\argumentC}{\argumentD}}

然后只需要扩展一次:

\expandafter\myMacro\tmp % \tmp already contains the braces

从相同的技巧框,但完全相反的问题,即在具有许多参数的宏的末尾扩展一个参数,可以通过将所有内容塞入临时宏中的参数来解决,然后可以很容易地“跳过”:

\def\B{B}
\def\tmp{\myMacro{argument A}{argument B}}
\expandafter\tmp\expandafter{\argumentC}

否则你需要做

\expandafter\myMacro\expandafter{\expandafter a\expandafter r\expandafter g …

答案2

这是对Qrrbrbirlbel 的回答在执行宏本身之前有选择地扩展宏的一个参数。我将在这里将“扩展”理解为扩展一次,并且不假设参数只是单个标记(扩展将仅应用于参数的第一个标记;这当然可能会引起该参数的进一步扩展)。我的评论是可以扩展地做到这一点。

注意:在下面的编辑中添加了更强大的“ expandable expander”。它也只扩展一次目标参数(第一个标记),但这可以提升为“完全扩展”。(见代码)

我们需要这些实用程序:

\def\expandArg #1#2{\expandafter\expandArgaux\expandafter{#2}{#1}}
\def\expandArgaux #1#2{#2{#1}}

\mymacro然后想象一下,在执行之前我需要将 5 个参数中的第 3 个展开一次\mymacro

\expandArg {\mymacro {Not me}{Not me}}{Expand Me}{Not me}{Not me} 

成功了。与Qrrbrbirlbel 的回答并且在评论中指出,这是可扩展的,因为不需要\def

为了显示:

\def\expandArg #1#2{\expandafter\expandArgaux\expandafter{#2}{#1}}
\def\expandArgaux #1#2{#2{#1}}

\tt
\def\mymacro #1#2#3#4#5{\meaning #3}
\def\x {\y}
\def\y {z}
\mymacro {1}{2}{\x}{4}{5}
\par
\expandArg {\mymacro {1}{2}}{\x}{4}{5}
\bye

输出:

扩展技巧

我在评论中提到,也可以使用更加用户友好的语法:

\ExpandNth {5}\mymacro{A}{B}{C}{D}{E}{F}{G}

没有多余的括号,因此,在执行之前仅扩展第五个参数\mymacro

这是一种方法。如果给定N零或更少,它会正确运行,但如果N超过实际参数数量,则会产生错误。同样重要的是,\mymacro应用可扩展扩展器的宏的参数之间不能留有空格。

\catcode`\_ 11

\def\ExpandNth #1%
{%
    \romannumeral0\ifnum #1>0
      \expandafter\ExpandNth_a
    \else
      \expandafter\ExpandNth_none
    \fi {#1}%
}
\def\ExpandNth_none #1{ }
\def\ExpandNth_a #1#2{\ExpandNth_b {#1}{#2}}
\def\ExpandNth_b #1%
{%
    \ifnum #1>1
      \expandafter\ExpandNth_c
    \else
      \expandafter\ExpandNth_w
    \fi {#1}%
}
\def\ExpandNth_c #1#2#3%
{%
    \expandafter\ExpandNth_b\expandafter
        {\the\numexpr #1-1}{#2{#3}}%
}%
\def\ExpandNth_w #1#2#3%
{%
    \expandafter\ExpandNth_z\expandafter {#3}{#2}%
}%
% replace #3 above by \romannumeral-`0#3 for "full" expansion

\def\ExpandNth_z #1#2{ #2{#1}}%

\catcode`\_ 8       

\tt

\def\mymacro #1#2#3#4#5{\meaning #1\par
                        \meaning #2\par
                        \meaning #3\par
                        \meaning #4\par
                        \meaning #5\par}
\def\a{\A} \def\A{A}
\def\b{\B} \def\B{B}
\def\c{\C} \def\C{C}
\def\d{\D} \def\D{D}
\def\e{\E} \def\E{E}

\hsize 12cm

Original \string\mymacro:\par
\mymacro \a\b\c\d\e

\medskip\hrule\medskip

With prior (once) expansion of the first argument:\par
\ExpandNth {1}\mymacro \a\b\c\d\e

\medskip\hrule\medskip

With prior (once) expansion of the second argument:\par
\ExpandNth {2}\mymacro \a\b\c\d\e

\medskip\hrule\medskip

With prior (once) expansion of the third argument:\par
\ExpandNth {3}\mymacro \a\b\c\d\e

\medskip\hrule\medskip

With prior (once) expansion of the fourth argument:\par
\ExpandNth {4}\mymacro \a\b\c\d\e

\medskip\hrule\medskip

With prior (once) expansion of the fifth argument:\par
\ExpandNth {5}\mymacro \a\b\c\d\e

\bye

输出:

选择性扩张

答案3

对于这个特定的应用程序你可以简单地

\newcommand{\nodexnX}[1]{\expandafter\nodexn\expandafter{#1}}

并调用\nodexnX{\points}{B}

或者你可以进一步重新定义,\nodexn以便它完全扩展其第一个参数

\let\PSTRICKSnodexn\nodexn
\renewcommand{\nodexn}[1]{%
  \begingroup\edef\x{\endgroup
  \noexpand\PSTRICKSnodexn{#1}}\x
}

LaTeX3 内核已\exp_args:Nx隐式添加了\cs_generate_variant:Nn

\usepackage{expl3}
\ExplSyntaxOn
\cs_set_eq:NN \pstricks_nodexn:nn \nodexn
\cs_generate_variant:Nn \pstricks_nodexn:nn { x }
\cs_set_eq:NN \nodexn \pstricks_nodexn:xn
\ExplSyntaxOff

如果你需要完全扩展第二个参数,你可以这样做

\cs_set_eq:NN \pstricks_nodexn:nn \nodexn
\cs_generate_variant:Nn \pstricks_nodexn:nn { xx }
\cs_set_eq:NN \nodexn \pstricks_nodexn:xx

在这两种情况下,您都\nodexn在调用原始宏之前有效地重新定义以扩展其参数。


2022 年新增

在下一个 LaTeX 内核中,它将可用\ExpandArgs,并且

\ExpandArgs{x}\nodexn{\points}{B}

就可以了。这是 的一个接口\exp_args:Nx,这意味着下一个标记将被跳过,并且后面括号组的内容将完全展开。

您还想充分扩展第二个论点吗?

\ExpandArgs{xx}\nodexn{\points}{\foo}

会做。

只要\exp_args:N<characters>存在,就可以使用\ExpandArgs{<characters>}。字符包括

  • N表示“跳过一个标记”
  • n表示“跳过支撑组”
  • x使用以下方法完全展开支撑组的内容\edef
  • e使用以下方法完全展开支撑组的内容\expanded
  • f递归地遍历支撑组的内容,直到找到不可展开的标记
  • o展开支撑组中的第一个标记

如果不存在所需的特定组合,则没有“官方”用户界面来创建它,但可以这样做

\ExplSyntaxOn
\exp_args_generate:n { <characters>, ... }
\ExplSyntaxOff

可以一次生成多个组合,字符串之间用逗号分隔。

答案4

functional包中,棘手的参数扩展可以用直观的函数组合来代替,这与其他编程语言类似,例如Lua

\documentclass[pstricks,border=12pt]{standalone}

\usepackage{pst-node}

\usepackage{functional}
\Functional{scoping=true} % make every function become a group

\begin{document}

\IgnoreSpacesOn
\PrgNewFunction \NodeXn { m m } {
  \nodexn {#1} {#2}
}
\IgnoreSpacesOff

\begin{pspicture}[showgrid](-5,-5)(5,5)
    \pnodes{A}(-2,3)(3,4)(2,-1)(-2,-4)(-4,0)
    \def\points{(0,0)}
    \foreach \x in {0,1,...,4}{%
            \pscircle*[linecolor=blue](A\x){3pt}%
            \xdef\points{\points+.2(A\x)}%
    }
    \NodeXn{\Value\points}{B}
    \pscircle*[linecolor=red](B){3pt}
\end{pspicture}

\end{document}

在此处输入图片描述

相关内容