将其参数的每个单词作为参数传递给另一个宏的宏?

将其参数的每个单词作为参数传递给另一个宏的宏?

我想要一个像这样工作的命令:

\dothings{Takes several words as argument}

并将其扩展为:

\@dothings{Takes} \@dothings{several} \@dothings{words}...

我知道如何定义一个简单的宏,将所有内容带到下一个空间,但我不知道如何使用它来应用它每个句子中的单词。有没有简单的方法可以做到这一点?

我也很好奇接下来的作为一个论点,但我想这可能是一个单独的问题......

答案1

\documentclass{article}
\usepackage{xcolor}
\makeatletter
\def\dothings#1{\color{blue}\expandafter\dothings@i#1 \@nil}
\def\dothings@i#1 #2\@nil{%
  \color{.!80}#1
  \ifx\relax#2\relax\else\dothings@i#2\@nil\fi}
\makeatother

\begin{document}
\dothings{Takes several words as argument}

\end{document}

在此处输入图片描述

答案2

LaTeX3 现在提供

\seq_set_split:Nnn<seq var>{<delimiter>} {<argument>}

分裂<argument>每次出现<delimiter>(并从每个参数的两端删除空格),并将结果放入<seq var>(LaTeX3 中的列表数据结构)。一旦您有了这个序列,您就可以将给定函数映射到其所有项目上。

\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\seq_new:N \l_foo_seq
\NewDocumentCommand{\dothings}{m}
  {
    \seq_set_split:Nnn \l_foo_seq {~} {#1}
    \seq_map_inline:Nn \l_foo_seq { \fbox{##1} }
  }
\ExplSyntaxOff
\begin{document}
\dothings{Takes several words as argument}
\end{document}

编辑:实际上你可以用 xparse 更快地完成它参数处理器(这里\SplitList)。

\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand {\dothings}
  { > { \SplitList { ~ } } m }
  { \tl_map_inline:nn {#1} { \fbox{##1} } }
\ExplSyntaxOff
\begin{document}
\dothings{Takes several words as argument}
\end{document}

答案3

Herbert 已经展示了如何用空格分隔单词。这里有一个非常类似的解决方案,可以防止深度\ifx ... \fi嵌套(当使用许多单词时,深度嵌套可能会产生错误):

\documentclass{minimal}
\begin{document}

\def\wordsloop#1{\wordsloopiter#1 \nil}
\def\wordsloopiter#1 #2\nil{%
  \do{#1} %
  \ifx&#2&% #2 is empty, then & equeals &
    \let\next\relax
  \else
    \def\next{\wordsloopiter#2\nil}% iterate
  \fi
  \next}

\def\do#1{(#1)}
\wordsloop{This is a sentence.}
% we have: (This) (is) (a) (sentence.)

\end{document}

然而,如果在论证中使用很多单词,它仍然会很慢。


#1如果删除上述定义后面的空格,则可以通过类似的方法获得标记上的循环。但它可以更简单:

\documentclass{minimal}
\begin{document}

\def\tokensloop#1{\tokensloopiter#1\nil}
\def\tokensloopiter#1{%
  \ifx\nil#1\else\do{#1}\expandafter\tokensloopiter\fi}

\def\do#1{(#1)}
\tokensloop{aa  bb}
% we have (a)(a)(b)(b)

\end{document}

您也可以直接使用包\toksloop中的命令etextools

请注意,空格将被忽略。


对于空格,可以使用 catcode 或其他技巧逐字读取字符。这更复杂。

例如,我们可以用来\obeyspaces改变空格的 catcode:

\documentclass{minimal}
\begin{document}

\def\charsloop{\begingroup\obeyspaces\charsloophelper}
\def\charsloophelper#1{\charsloopiter#1\nil\endgroup}
\def\charsloopiter#1{%
  \ifx\nil#1\else\do{#1}\expandafter\charsloopiter\fi}

\def\do#1{(#1)}
\charsloop{aa  bb}
% we have (a)(a)( )( )(b)(b)

\end{document}

(换行符仍然会被忽略\obeyspaces。)

答案4

ConTeXt 提供了一个宏\processisolatedwords来实现这一点。例如,要在每个单词周围画一个框,你可以使用

\processisolatedwords {\input ward \relax} \inframed

在 MkIV 中,这个宏在 lua 中使用以下函数实现:

local function applytowords(list,what,nested)
    local doaction = context[what or "ruledhbox"]
    local noaction = context
    local current  = checkedlist(list)
    local start
    while current do
        local id = current.id
        if id == glue_code then
            if start then
                doaction(copynodelist(start,current))
                start = nil
            end
            noaction(copynode(current))
        elseif nested and (id == hlist_code or id == vlist_code) then
            context.hbox()
            context.bgroup()
            applytowords(current.list,what,nested)
            context.egroup()
        elseif not start then
            start = current
        end
        current = current.next
    end
    if start then
        doaction(copynodelist(start))
    end
end

也可以看看:

  • \applytowords
  • \processwords\applytosplitstringword

相关内容