我想使用用户作为参数提供的内联代码来修改/扩展预先存在的宏的集合。
假设我们有多个变量,例如:
\def\mon{Monday}
\def\tue{Tuesday}
我们需要定义它们的红色版本:
\def\redmon{\textcolor{red}{\mon}}
\def\redtue{\textcolor{red}{\tue}}
我们希望有一个命令可以为我们完成这项工作,如下所示:
\newCommandForEach{mon,tue}{red#1}{\textcolor{red}{#1}}
使用 expl3 我设法编写了简单的变体,它不会将令牌提供给内联代码,而是提供其名称:
\NewDocumentCommandRaw{\newCommandForEach}{mmm}{
\clist_set:Nn\l_tmpa_clist{#1} % Parse list
\clist_map_inline:Nn\l_tmpa_clist{
\cs_set:cpn{#2}{#3}
}
}
\newCommandForEachRaw{mon,tue}{red#1}{\textcolor{red}{#1}}
% defines the following:
\newcommand{\redmon}{\textcolor{red}{mon}}
% should be \mon -----------------^^^
如果我将内联命令存储在宏中,那么我就可以链式地向其提供适当的标记,但我不知道如何正确扩展必要的标记,以便连续调用不会重写先前的定义。以下调用还采用可选参数,该参数应指定将用作输入标记列表的 csname 的内联函数。
\NewDocumentCommand{\jpkNewCommandForEachRaw}{mmm}{
\clist_set:Nn\l_tmpa_clist{#1} % Parse list
\cs_set:Npn\jpk_NewCommandForEach_name_cs:n##1{#2}
\cs_set:Npn\jpk_NewCommandForEach_body_cs:n##1{#3}
\clist_map_inline:Nn\l_tmpa_clist{
\tl_set:Nn\jpk_NewCommandForEach_name_tl{#2}
\cs_set:cpn\jpk_NewCommandForEach_name_tl{
\jpk_NewCommandForEach_body_cs:n{##1}
}
}
}
\NewDocumentCommand{\jpkNewCommandForEach}{mmmO{##1}}{
\cs_set:Npn\jpk_NewCommandForEach_bodyraw_cs:n##1{#3}
\jpkNewCommandForEachRaw{#1}{#2}{
\jpk_NewCommandForEach_bodyraw_cs:n{\tl_use:c{#4}}
}
}
%
\jpkNewCommandForEachN{mon,tue}{red#1}{\textcolor{red}{#1}}[uncolored#1]
\jpkNewCommandForEachN{mon,tue}{blue#1}{\textcolor{blue}{#1}}[uncolored#1]
此后,命令 \redmon 会给出蓝色的“星期一”,因为某些内容已被覆盖。如果应用扩展,则不可扩展的 \textcolor 无法编译。
如何创建采用扩展参数的(未扩展的)内联主体函数?
答案1
下面的代码可以满足您的要求,我使用了一个组来包含三个辅助宏的临时定义,这可能并不是真正必要的。因此,我使用了 expandable\clist_map_function:nN
而不是map_inline
,但这实际上不应该成为问题(对于 -maps 来说,性能差异非常小clist
,对于 s 来说则不同seq
,但如果您不需要它用于其他任何用途,则将 解析clist
为seq
a 会再次变慢)。
\ExplSyntaxOn
\cs_new:Npn \__jpk_NewCommandForEach_foreach:n #1
{
\cs_set:Npn
\exp_not:c { \__jpk_NewCommandForEach_new_name:n {#1} }
{
\exp_args:Nc \__jpk_NewCommandForEach_code:N
{ \__jpk_NewCommandForEach_rel_name:n {#1} }
}
}
\NewDocumentCommand \jpkNewCommandForEach { m m m O{##1} }
{
\group_begin:
\cs_set:Npn \__jpk_NewCommandForEach_new_name:n ##1 {#2}
\cs_set:Npn \__jpk_NewCommandForEach_code:N ##1 { \exp_not:n {#3} }
\cs_set:Npn \__jpk_NewCommandForEach_rel_name:n ##1 {#4}
\exp_last_unbraced:Ne
\group_end:
{ \clist_map_function:nN {#1} \__jpk_NewCommandForEach_foreach:n }
}
\ExplSyntaxOff
\newcommand*\uncoloredmon{Montag}
\newcommand*\uncoloredtue{Tuesday}
\jpkNewCommandForEach{mon,tue}{red#1}{\textcolor{red}{#1}}[uncolored#1]
\jpkNewCommandForEach{mon,tue}{blue#1}{\textcolor{blue}{#1}}[uncolored#1]
\show\redmon
结果是
> \redmon=\long macro:
->\textcolor {red}{\uncoloredmon }.
答案2
我稍微修改了输入语法。我还让它扩展了定义,\redmon
因此
\textcolor {red}{Monday}
而不是\textcolor {red}{\mon}
如果您需要保留扩展,因为\mon
可以安排更多,\use:whatever
但如果不需要,这种形式可能是最简单的。
\documentclass{article}
\usepackage{color}
\begin{document}
\def\mon{Monday}
\def\tue{Tuesday}
\ExplSyntaxOn
\NewDocumentCommand\newCommandForEach{mmm}{
\clist_map_inline:nn{#1}{
\cs_set:cpe{#2##1}{\exp_not:N#3{#2}{\use:c{##1}}}
}
}
\ExplSyntaxOff
\newCommandForEach{mon,tue}{red}{\textcolor}
%\show\redmon
\redmon
\end{document}
答案3
我\adorn
用三个参数来定义:要装饰的“变量”列表、应用于名称的前缀和“adorned”命令的模板,其中#1
代表循环中当前变量的值。
\documentclass{article}
\usepackage{xcolor}
\ExplSyntaxOn
\NewDocumentCommand{\adorn}{mmm}
{% #1 = list of parameterless commands
% #2 = prefix
% #3 = template
\foo_adorn:nnn { #1 } { #2 } { #3 }
}
\cs_new_protected:Nn \__foo_adorn_do:nn {}
\cs_new_protected:Nn \foo_adorn:nnn
{
\cs_set_protected:Nn \__foo_adorn_do:nn { \cs_set:cpn { #2 ##2 } { #3 } }
\clist_map_inline:nn { #1 }
{
\exp_args:Ne \__foo_adorn_do:nn { \exp_not:c { ##1 } } { ##1 }
}
}
\ExplSyntaxOff
\def\mon{Monday}
\def\tue{Tuesday}
\adorn{mon,tue}{red}{\textcolor{red}{#1}}
\begin{document}
\mon, \tue
\redmon, \redtue
\texttt{\meaning\redmon}
\texttt{\meaning\redtue}
\end{document}
如果我这样做
\adorn{mon,tue}{red}{--#1--}
输出将是
请注意,如果您改变的含义,\mon
您也会自动改变的含义\redmon
。
如果这是不想要的,那么用 替换\exp_not:c
。\exp_not:v
使用\textcolor{red}
包装器,你会得到