我正在尝试使用以下代码在 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}
这会产生两个错误:
! \test 代码定义中的参数数量非法。
! 在水平模式下不能使用“宏参数字符 #”。
我正在尝试实现这样的输出:
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
处理 时,这些符号被保存到宏主体中,而不对周围的标记进行任何解释。这意味着您不能使用#\foo
where\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}