用于在逗号分隔的列表中应用自定义命令的宏

用于在逗号分隔的列表中应用自定义命令的宏

我想创建一个apply函数,可以将任何单参数命令应用于以逗号分隔的参数列表,并打印以逗号分隔的结果。 (ETA:我想从头开始创建命令,即不使用其他包依赖项,作为了解所有子例程如何工作的一种方式。)

我有一个部分可行的解决方案,但当要应用的命令很复杂时,它不起作用。请参见以下示例,其中\lowercase可以有效地应用于列表,但\ComplicatedCommand\MoreComplicatedCommand不能:

\RequirePackage{luatex85}
\documentclass[letterpaper]{article}


% This command is for applying a single command to a comma-separated list of tokens, and listing the results separated by ", "
\makeatletter
\newcommand{\apply}[3][, ]{
% #1: optional separator to print between applications; default=[, ]
% #2: command to apply; 
% #3: list to apply command to 
  \def\itemsep{\def\itemsep{#1}} % first call to \itemsep prints nothing; later calls print #1
  \@for \listelement:=#3\do{\itemsep#2\expandafter{\listelement}}%
}
\makeatother

\begin{document}

\apply{\lowercase}{THESE,WORDS,PRINT,IN,LOWERCASE.}

\newcommand{\ComplicatedCommand}[1]{\lowercase{#1}}
\apply{\ComplicatedCommand}{WHY,DO,THESE,WORDS,NOT,PRINT,IN,LOWERCASE?}

\newcommand{\MoreComplicatedCommand}[1]{\lowercase{\uppercase{{\lowercase{#1}}}}}
\apply{\MoreComplicatedCommand}{ULTIMATELY,I,WANT,APPLY,TO,MAKE,THESE,LOWERCASE,AS,WELL.}


\end{document}

结果:

示例结果

有人能修复我的\newcommand{\apply}代码,让它可以作为任何\ComplicatedCommand参数使用#2吗?如果可以,请提供一个完整的工作示例,从 到\documentclass\end{document}以便将来阅读答案的任何人都可以复制并粘贴它,以查看它是否仍然适用于他们的 (Lua)TeX 版本。

答案1

你有

\itemsep#2\expandafter

这意味着,如果#2是一个带有参数的宏,就像您的后两种情况一样(\lowercase不是宏),那么参数将是\expandafter。您想\expandafter在扩展 之前进行扩展#2。如果您知道#2始终是一个单个标记,那么您可以在它前面放置额外的标记\expandafter,但更安全的做法是:

所以...

\documentclass[letterpaper]{article}


% This command is for applying a single command to a comma-separated list of tokens, and listing the results separated by ", "
\makeatletter
\newcommand{\apply}[3][, ]{
% #1: optional separator to print between applications; default=[, ]
% #2: command to apply; 
% #3: list to apply command to 
  \def\itemsep{\def\itemsep{#1}} % first call to \itemsep prints nothing; later calls print #1
  \def\zz{\itemsep#2}%
  \@for \listelement:=#3\do{\expandafter\zz\expandafter{\listelement}}%
}
\makeatother

\begin{document}

\apply{\lowercase}{THESE,WORDS,PRINT,IN,LOWERCASE.}

\newcommand{\ComplicatedCommand}[1]{\lowercase{#1}}
\apply{\ComplicatedCommand}{WHY,DO,THESE,WORDS,NOT,PRINT,IN,LOWERCASE?}

\newcommand{\MoreComplicatedCommand}[1]{\lowercase{\uppercase{{\lowercase{#1}}}}}
\apply{\MoreComplicatedCommand}{ULTIMATELY,I,WANT,APPLY,TO,MAKE,THESE,LOWERCASE,AS,WELL.}


\end{document}

答案2

etoolbox已经具备列表处理能力,那么为什么不直接利用这一点呢:

在此处输入图片描述

\documentclass{article}

\usepackage{etoolbox}

% This command is for applying a single command to a comma-separated list of tokens, and listing the results separated by ", "
\newcommand{\apply}[3][, ]{%
  % #1: optional separator to print between applications; default = [, ]
  % #2: command to apply; 
  % #3: list to apply command to 
  \def\listitemsep{\def\listitemsep{#1}}% https://tex.stackexchange.com/a/89187/5764
  \renewcommand{\do}{\listitemsep #2}% Each item should be processed this way
  \docsvlist{#3}% Process entire list of items
}

\begin{document}

\apply{\MakeLowercase}{THESE,WORDS,PRINT,IN,LOWERCASE.}

\newcommand{\ComplicatedCommand}[1]{\MakeLowercase{#1}}%
\apply{\ComplicatedCommand}{WHY,DO,THESE,WORDS,NOT,PRINT,IN,LOWERCASE?}

\newcommand{\MoreComplicatedCommand}[1]{\MakeLowercase{\MakeUppercase{{\MakeLowercase{#1}}}}}%
\apply{\MoreComplicatedCommand}{ULTIMATELY,I,WANT,APPLY,TO,MAKE,THESE,LOWERCASE,AS,WELL.}

\end{document}

每个元素都用 进行处理\do,而整个列表则使用 按顺序进行处理\docsvlist

答案3

使用 的更简洁的代码expl3;请注意输入中的前导空格和尾随空格是如何被忽略的。这个想法是解析逗号分隔的项目列表并将每个项目添加到序列中,但作为给定命令的参数。然后我们可以输出带有所需分隔符的列表。

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn

\NewDocumentCommand{\apply}{O{,~}mm}
 {% #1 = output separator, #2 = command to apply, #3 = list
  \critch_apply:Nnn { #2 } { #1 } { #3 }
 }

\seq_new:N \l_critch_apply_output_seq

\cs_new_protected:Nn \critch_apply:Nnn
 {
  \seq_clear:N \l_critch_apply_output_seq
  \clist_map_inline:nn { #3 }
   {
    \seq_put_right:Nn \l_critch_apply_output_seq { #1{##1} }
   }
  \seq_use:Nn \l_critch_apply_output_seq { #2 }
 }

\ExplSyntaxOff

\newcommand{\ComplicatedCommand}[1]{\lowercase{#1}}
\newcommand{\MoreComplicatedCommand}[1]{\lowercase{\uppercase{{\lowercase{#1}}}}}

\begin{document}

\apply{\lowercase}{THESE,WORDS,PRINT,IN,LOWERCASE.}

\apply{\ComplicatedCommand}{WHY,DO,THESE,WORDS,NOT,PRINT,IN,LOWERCASE?}

\apply[~--- ]{\MoreComplicatedCommand}{ULTIMATELY , I , WANT,APPLY ,TO,
  MAKE,THESE,LOWERCASE,AS,WELL.}

\end{document}

在此处输入图片描述

扩展版本中,第二个参数也可以是一组应用于每个项目的指令,其中项目由 表示#1;如果第二个参数由单个宏组成,则假定它是您想要应用于每个项目的指令。第一个例子可以按照与

\apply{\lowercase{#1}}{THESE,WORDS,PRINT,IN,LOWERCASE.}

这是代码。

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn

\NewDocumentCommand{\apply}{O{,~}+mm}
 {% #1 = output separator, #2 = command to apply, #3 = list
  \critch_apply:nnn { #1 } { #2 } { #3 }
 }

\seq_new:N \l_critch_apply_output_seq

\cs_new_protected:Nn \critch_apply:nnn
 {
  \seq_clear:N \l_critch_apply_output_seq
  \bool_lazy_and:nnTF { \tl_if_single_p:n { #2 } } { \token_if_cs_p:N #2 }
   {
    \cs_gset_eq:NN \__critch_apply_command:n #2
   }
   {
    \cs_gset:Nn \__critch_apply_command:n { #2 }
   }
  \clist_map_inline:nn { #3 }
   {
    \seq_put_right:Nn \l_critch_apply_output_seq { \__critch_apply_command:n {##1} }
   }
  \seq_use:Nn \l_critch_apply_output_seq { #1 }
 }

\ExplSyntaxOff

\newcommand{\ComplicatedCommand}[1]{\lowercase{#1}}
\newcommand{\MoreComplicatedCommand}[1]{\lowercase{\uppercase{{\lowercase{#1}}}}}

\newcommand{\surround}[3]{#1\textit{#3}#2}

\begin{document}

\apply{\lowercase}{THESE,WORDS,PRINT,IN,LOWERCASE.}

\apply{\ComplicatedCommand}{WHY,DO,THESE,WORDS,NOT,PRINT,IN,LOWERCASE?}

\apply[~--- ]{\MoreComplicatedCommand}{ULTIMATELY , I , WANT,APPLY ,TO,
  MAKE,THESE,LOWERCASE,AS,WELL.}

\apply[ $|$ ]{(#1)}{a, list,of , words}

\begin{tabular}[t]{@{}c@{}}
\apply[\\]{\surround{-}{!}{#1}}{a, list,of , words}
\end{tabular}

\end{document}

在此处输入图片描述

答案4

为...量身定制listofitems

\documentclass{article}
\usepackage{listofitems}
\newcommand\apply[2]{%
  \readlist*\arglist{#2}%
  \foreachitem\i\in\arglist{\expandafter#1\expandafter{\i}}%
}
\begin{document}
\newcommand{\ComplicatedCommand}[1]{\lowercase{#1}}
\apply{\ComplicatedCommand}{WHY,DO,THESE,WORDS,NOT,PRINT,IN,LOWERCASE?}

\newcommand{\MoreComplicatedCommand}[1]{\lowercase{\uppercase{{\lowercase{#1}}}}}
\apply{\MoreComplicatedCommand}{ULTIMATELY,I,WANT,APPLY,TO,MAKE,THESE,LOWERCASE,AS,WELL.}
\end{document}

在此处输入图片描述

显然,在循环中添加一个空格\foreachitem,例如

\foreachitem\i\in\arglist{\expandafter#1\expandafter{\i} }%

将在循环输出之间添加一个空格:

在此处输入图片描述

相关内容