![用于在逗号分隔的列表中应用自定义命令的宏](https://linux22.com/image/368506/%E7%94%A8%E4%BA%8E%E5%9C%A8%E9%80%97%E5%8F%B7%E5%88%86%E9%9A%94%E7%9A%84%E5%88%97%E8%A1%A8%E4%B8%AD%E5%BA%94%E7%94%A8%E8%87%AA%E5%AE%9A%E4%B9%89%E5%91%BD%E4%BB%A4%E7%9A%84%E5%AE%8F.png)
我想创建一个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} }%
将在循环输出之间添加一个空格: