定义一个命令,将内联函数应用于列表中的每个元素,并将结果保存到多个命令

定义一个命令,将内联函数应用于列表中的每个元素,并将结果保存到多个命令

我想使用用户作为参数提供的内联代码来修改/扩展预先存在的宏的集合。

假设我们有多个变量,例如:

\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,但如果您不需要它用于其他任何用途,则将 解析clistseqa 会再次变慢)。

\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}包装器,你会得到

在此处输入图片描述

相关内容