如何在 for 循环中迭代宏参数?或者如何使用计数器扩展宏参数?

如何在 for 循环中迭代宏参数?或者如何使用计数器扩展宏参数?

我正在尝试使用以下代码在 for 循环中使用计数器扩展宏参数:

\documentclass{article}
\usepackage{xparse}
\usepackage{forloop}

\NewDocumentCommand\test{m O{II} O{III} O{IV}}{
  \newcounter{i}
  \forloop{i}{1}{\thei < 5}{
    #1\thei : #\thei \quad
  }
}

\begin{document}

\test{A}

\end{document}

这会产生两个错误:

  1. ! \test 代码定义中的参数数量非法。

  2. ! 在水平模式下不能使用“宏参数字符 #”。

我正在尝试实现这样的输出:

A1:A  A2:II  A3:III  A4:IV

即我想遍历宏参数。

看来问题出在零件上#\thei。所以到目前为止我尝试过的一些方法都\expandafter #\thei没有\expandafter #\numexpr\thei\relax成功。

我一直在寻找TeX 按主题分类但我找不到任何有用的信息。如果可能的话,我将不胜感激!


在这里我留下一个完全编译的变体用于测试目的,这当然不是所需的行为:

\documentclass{article}
\usepackage{xparse}
\usepackage{forloop}

\NewDocumentCommand\test{m O{II} O{III} O{IV}}{
  \newcounter{i}
  \forloop{i}{1}{\thei < 5}{
    #1\thei : #1 \quad
  }
}

\begin{document}

\test{A}

\end{document}

输出:

A1:A  A2:A  A3:A  A4:A

答案1

宏主体中的符号#1,#2被解释为“必须插入实际参数的点”,并且在\def处理 时,这些符号被保存到宏主体中,而不对周围的标记进行任何解释。这意味着您不能使用#\foowhere\foo是扩展为 的宏1

您的任务可以通过以下宏来解决:

\newcount\tmpnum
\def\test #1{\tmpnum=0 \def\param{#1}\testA{}{II}{III}{IV}\end}
\def\testA #1{\ifx\end#1\relax\else
   \advance\tmpnum by1
   \param\the\tmpnum:\ifx^#1^\param \else#1\fi \space
   \expandafter\testA \fi
}

\test{A}

\bye

答案2

当 TeX 吸收宏的替换文本时,它不会以任何方式解释标记。使用\edef也不成问题,因为\edef替换文本中不会执行赋值。

不过,您可以使用间接方法。

\documentclass{article}

\ExplSyntaxOn

% we need a variant
\cs_generate_variant:Nn \cs_new:Nn { NV }

% the template: first argument, index, colon, argument corresponding to index
\cs_new:Nn \__alvaro_aux:n { ##1#1:###1~ }

% set a tl to iterate the desired template
\exp_args:NNe \tl_set:Nn \l_tmpa_tl { \int_step_function:nN { 4 } \__alvaro_aux:n }

% define an internal function; \use:n is needed to reduce the number of #
\use:n { \cs_new:NV \alvaro_test:nnnn \l_tmpa_tl }

% the external function
\NewDocumentCommand{\test}{mO{II}O{III}O{IV}}
 {
  \alvaro_test:nnnn { #1 } { #2 } { #3 } { #4 }
 }

\ExplSyntaxOff

\begin{document}

\test{A}

\test{B}[1]

\test{C}[1][2]

\test{D}[1][2][3]

\end{document}

嗯,这很有趣,因为你可以看到###1(三个#字符并不常见)。

在此处输入图片描述

答案3

你可以尝试 expl3

\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand\test{m O{II} O{III} O{IV}}{
\clist_set:Nn \l_tmpa_clist{#1,#2,#3,#4}
\int_step_inline:nn{4}
{
    #1##1: \clist_item:Nn \l_tmpa_clist{##1}\quad
}
}
\ExplSyntaxOff
\begin{document}

\test{A}\par
\test{A}[B][C][D]

\end{document}

在此处输入图片描述

答案4

只要你不指定为什么如果您希望迭代,我建议不用迭代:

\documentclass{article}

\NewDocumentCommand\test{m O{II} O{III} O{IV}}{%
  #11:#1\quad#12:#2\quad#13:#3\quad#14:#4%
}

\begin{document}

\test{A}

\test{B}[1]

\test{C}[1][2]

\test{D}[1][2][3]

\end{document}

在此处输入图片描述


或者,您可以有一个循环来处理默认值列表,在每次迭代中抓取一个可选参数,如果其值等于 -marker,则-NoValue-提供来自列表的默认值,否则提供该可选参数:

\documentclass{article}

\makeatletter
\NewDocumentCommand\test{m}{%
  #11:#1\testloop{2}{#1}{II}{III}{IV}%<- here you can append more default-values if you wish TeX to process more optional arguments.
  {}{}\relax
}%
\@ifdefinable\testloop{%
  \long\def\testloop#1#2#3#4\relax{%
    \ifcat$\detokenize{#4}$\expandafter\@gobble\else\expandafter\@firstofone\fi
    {\testloopopt{#1}{#2}{#3}{#4}}%
  }%
}%
\NewDocumentCommand\testloopopt{mmmmo}{%
  \quad#2#1:\IfNoValueTF{#5}{#3}{#5}%
  \expandafter\testloop\expandafter{\number\numexpr#1+1\relax}{#2}#4\relax
}%
\makeatother

\begin{document}

\test{A}

\test{B}[1]

\test{C}[1][2]

\test{D}[1][2][3]

\end{document}

在此处输入图片描述

相关内容