我有名为\cta
、\ctb
、ctc
、的命令\ctd
。
我想在循环中调用这些命令,如下所示:
\foreach \i in {a,b,c,d}{
% this is where I want to call the command
}
我怎样才能实现这个目标?
答案1
TeX 原语\csname...\endcsname
允许构建宏名。
\documentclass{article}
\usepackage{tikz}
\newcommand*{\cta}{A}
\newcommand*{\ctb}{BB}
\newcommand*{\ctc}{CCC}
\newcommand*{\ctd}{DDDD}
\begin{document}
\foreach \i in {a,b,c,d}{The content of \texttt{\expandafter\string\csname ct\i\endcsname} is \csname ct\i\endcsname.\par}
\end{document}
当 TeX 遇到
\csname ct\i\endcsname
它会不断扩展\i
(并持续扩展)直到只获得字符标记,并使用相应的“字符串”构建控制序列。(如果扩展产生非字符标记,则会引发错误。)
答案2
一个相当通用的循环:
documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\loopvar}{m +O{} m +O{}}
{
\clist_map_inline:nn { #3 } { #2 \use:c { #1##1 } #4 }
}
\ExplSyntaxOff
\newcommand{\cta}{\texttt{\string\cta}}
\newcommand{\ctb}{\texttt{\string\ctb}}
\newcommand{\ctc}{\texttt{\string\ctc}}
\newcommand{\ctd}{\texttt{\string\ctd}}
\begin{document}
\loopvar{ct}{a,b,c,d}
\loopvar{ct}[This is ]{a,b,c}[\par]
\end{document}
第一个必选参数是公共部分,第二个必选参数是需要附加的字符串列表;第一个可选参数是在宏前添加的“前缀”,最后一个是“后缀”。
说明:\use:c
从其参数构建一个控制序列名称,本质上是\csname...\endcsname
伪装的;\clist_map_inline:nn
映射逗号分隔的列表,为每个项目执行第二个参数中指定的代码(#1
代表当前项目,但这里我们需要使用,##1
因为我们正在定义一个宏)。
答案3
\cta
由于在 TeX 中一切都与可扩展的标记有关,因此您将面临有关、\ctb
、\ctc
的扩展\ctd
发生的时间顺序的问题。
情况1:
循环是否传递整个标记序列\cta\ctb\ctc\ctd
?
(如果是\cta
处理三个非分隔参数的宏,则其第一个参数是标记\ctb
,其第二个参数是标记\ctc
,其第三个参数是标记\ctd
。)
案例 2:
\cta
是不是要在第一次迭代中完全生成、扩展并执行该令牌,然后\ctb
在第二次迭代中完全生成、扩展并执行该令牌,然后在\ctc
第三次迭代中完全生成、扩展并执行该令牌,然后\ctd
在第四次迭代中完全生成、扩展并执行该令牌?
因此我们重点讨论第一种情况:
您可以使用临时宏来积累令牌:
\documentclass{article}
\usepackage{tikz}
\newcommand*{\cta}[3]{%
\noindent
\texttt{\string\cta}'s first argument is: \texttt{\string#1}\\%
\texttt{\string\cta}'s second argument is: \texttt{\string#2}\\%
\texttt{\string\cta}'s third argument is: \texttt{\string#3}%
\bigskip
#1#2#3%
}
\newcommand*{\ctb}[2]{%
\noindent
\texttt{\string\ctb}'s first argument is: \texttt{\string#1}\\%
\texttt{\string\ctb}'s second argument is: \texttt{\string#2}%
\bigskip
#1#2%
}
\newcommand*{\ctc}[1]{%
\noindent
\texttt{\string\ctc}'s first argument is: \texttt{\string#1}%
\bigskip
#1%
}
\newcommand*{\ctd}{%
\noindent
\texttt{\string\ctd} does not process arguments.%
}
\newcommand\scratchmacro{}%
\begin{document}
\def\scratchmacro{}
\foreach \i in {a,b,c,d}{%
\csname g@addto@macro%
\expandafter\endcsname
\expandafter{%
\expandafter\scratchmacro
\expandafter}%
\expandafter{%
\csname ct\i\endcsname
}%
}%
\noindent
\texttt{\string\scratchmacro: \meaning\scratchmacro}
\bigskip
\scratchmacro
\end{document}
如果您希望有一个完全可扩展的循环,那么您将面临可扩展的逗号列表解析的任务。
在这种情况下,你可能会对这个问题的答案感兴趣\newcommand
有很多争论。
如果您希望有一个完全可扩展的循环,但可以使用非分隔参数列表而不是逗号列表来制作它,即类似于{a}{b}{c}{d}
而不是a,b,c,d
,那么您将面临可扩展参数列表解析的任务。
在这种情况下,你可能会对这个问题的一些答案感兴趣在循环中用变量名定义新命令。
另一种方法可能是:
\documentclass{article}
\makeatletter
\newcommand\UD@exchange[2]{#2#1}%
\newcommand\nameloop{\romannumeral0\UD@innernameloop}%
\newcommand\UD@innernameloop[3]{%
\ifx\relax#3\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
{ #2}{%
\expandafter\UD@exchange
\expandafter{%
\expandafter{%
\romannumeral0%
\expandafter\UD@exchange\expandafter{%
\csname#1#3\endcsname
}{ #2}%
}}{%
\UD@innernameloop{#1}%
}%
}%
}%
\makeatother
\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\scratchmacro
\expandafter\expandafter\expandafter{%
\nameloop{ct}{Tokens in front}{a}{b}{c}{d}{\relax}%
}%
\begin{document}
\texttt{\string\scratchmacro: \meaning\scratchmacro}
\end{document}