在将命令附加到另一个宏之前,先展开命令的所有参数

在将命令附加到另一个宏之前,先展开命令的所有参数
\documentclass{article}
\makeatletter
\usepackage{ltxcmds}

\newcounter{mycounter}
\setcounter{mycounter}{7}

\newcommand{\mysndc}[3]{#1 #2 #3}

\newcommand{\mycommand}{}

\newcommand{\mytrdc}[1]{\ltx@GlobalAppendToMacro{\mycommand}{%
\mysndc{\arabic{mycounter}}{#1}{\thepage}}}

\makeatother
\begin{document}
x

\mytrdc{8}

\newpage
\setcounter{mycounter}{4}

\mycommand
\end{document}

\mycommand调用时,\mysndc{\arabic{mycounter}}{8}{\thepage}结果是4 8 2。我希望结果是\mysndc{7}{8}{2}7 8 1为了实现这一点,必须在将参数添加到\mysndc之前扩展。应该是有用的,但它的不同位置(也不止一个)并没有产生预期的结果,例如\mysndc\mycommand\expandafter\expandafter

\newcommand{\mytrdc}[1]{\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\ltx@GlobalAppendToMacro{\mycommand\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter}{\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\mysndc\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter{\expandafter\expandafter\expandafter\arabic{mycounter}\expandafter\expandafter\expandafter}\expandafter\expandafter\expandafter{\expandafter#1\expandafter}\expandafter{\thepage}}}

。在将其附加到之前,我必须在哪里放置多少个\expandafters 来扩展它的参数,以及具体原因是什么?我在 tex.SE 上找到了许多类似的问题和答案,但仍然无法正确回答。如果可能的话,我更喜欢使用s 的解决方案,这样我就可以从中学习,而无需使用 e-TeX/etextools/etextoolbox/LaTeX3/...\mysndc\mycommandexpandafter

答案1

解决这个问题的最简单方法可能是使用强制扩展所有内容\edef,因为这样我们就避免了需要改组参数。 (\mytrdc在任何情况下都是不可扩展的,因此我们可以使用赋值。)一种可能的方法是使用\edef

\documentclass{article}

\usepackage{ltxcmds}

\newcounter{mycounter}
\setcounter{mycounter}{7}

\newcommand{\mysndc}[3]{#1 #2 #3}

\newcommand{\mycommand}{}
\makeatletter
\newcommand{\mytrdc}[1]{%
  \begingroup
  \edef\x{%
    \endgroup
    \noexpand\ltx@GlobalAppendToMacro{\noexpand\mycommand}{%
    \noexpand\mysndc{\arabic{mycounter}}{#1}{\thepage}}}%
  \x}

\makeatother
\begin{document}
x

\mytrdc{8}

\newpage
\setcounter{mycounter}{4}

\show\mycommand
\end{document}

这个想法很简单:创建一个临时的\edefed 宏\x,其中包含所需的扩展材料以及必要的“设置”,然后执行它。一切都是在一个组中完成的,这样我们就不会弄乱 的任何其他含义\x

答案2

可以用 来做\expandafter,但是它会很长,因为从 到\arabic{mycounter}7 需要四步扩展:

\arabic{mycounter}
\expandafter \@arabic \csname c@mycounter\endcsname
\@arabic \c@mycounter
\number \c@mycounter
7

扩展\thepage需要多一步,因为第一级扩展是\arabic{page}

让我们看一个更简单的例子。你有

\newcommand{\mymacro}[1]{\dosomething{#1}}

你的“附加”宏在哪里\dosomething,你想要的#1是参数扩展后的“最终”结果。所以,如果你有

\def\foo{A}

你想要的\dosomethingA,而不是\foo。这很容易:

\expandafter\mymacro\expandafter{\foo}

如果你有

\def\foo{\baz}\def\baz{A}

您需要扩展\foo两次:

\expandafter\expandafter\expandafter\mymacro
\expandafter\expandafter\expandafter{\foo}

一般来说,如果你想要 n 个扩展,你需要 2 n -1 个\expandafter标记。在 的情况下,\arabic{mycounter}你需要 之前和 之后各 15 个标记\mymacro。因为\thepage它们将是 31 个。但是由于有三个参数,事情变得复杂了!

因此需要一种不同的方法。最简单的方法是使用\edef或,以防您的参数可能包含诸如 之类的“危险”内容\textbf,则使用\protected@edef;我将以与 Joseph 的答案略有不同的方式进行操作,只是为了好玩。

\documentclass{article}

\usepackage{etoolbox}

\newcounter{mycounter}
\setcounter{mycounter}{7}

\newcommand{\mysndc}[3]{#1 #2 #3}

\newcommand{\mycommand}{}

\makeatletter
\newcommand{\mytrdc}[1]{%
  \begingroup
  \protected@edef\x{{\arabic{mycounter}}{#1}{\thepage}}%
  \expandafter\endgroup
  \expandafter\gappto\expandafter\mycommand\expandafter
    {\expandafter\mysndc\x}%
}

\makeatother
\begin{document}
x

\mytrdc{8}

\newpage
\setcounter{mycounter}{4}

\texttt{\meaning\mycommand}
\end{document}

临时宏\x将扩展为{7}{8}{1},并且在执行 的链式操作后它将消失(扩展\expandafter后该组将结束) 。\x

这是使用 LaTeX3 的另一种方法。

\documentclass{article}
\usepackage{xparse}

\newcounter{mycounter}
\newcommand{\mysndc}[3]{#1 #2 #3}

\ExplSyntaxOn
\NewDocumentCommand{\mytrdc}{ m }
 {
  \stephen_mytrdc:n { #1 }
 }
\NewDocumentCommand{\mycommand}{ }
 {
  \tl_use:N \g_stephen_list_tl
 }

\tl_new:N \g_stephen_list_tl

\cs_new_protected:Npn \stephen_mytrdc:n #1
 {
  \stephen_appto_mycommand:ffn { \arabic{mycounter} } { \thepage } { #1 }
 }

\cs_new_protected:Npn \stephen_appto_mycommand:nnn #1 #2 #3
 {
  \tl_gput_right:Nn \g_stephen_list_tl { \mysndc { #1 } { #3 } { #2 } }
 }
\cs_generate_variant:Nn \stephen_appto_mycommand:nnn { ff }
\ExplSyntaxOff

\begin{document}
\setcounter{mycounter}{7}

x

\mytrdc{8}

\newpage
\setcounter{mycounter}{4}
y

\mytrdc{42}

\expandafter\show\csname g_stephen_list_tl\endcsname
\end{document}

在实际文档中,你应该使用\mycommand而不是\show。参数的双重反转是为了提高效率。终端上的输出将是

> \g_stephen_list_tl=macro:
->\mysndc {7}{8}{1}\mysndc {4}{42}{2}.

相关内容