I'd like a command that works like this:

\dothings{Takes several words as argument}

and have this expand to:

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

I know how to define a simple macro that takes everything up to the next space, but I don't know how to use that to apply it to each word in a sentence. Is there a simple way to do this?

I'm also curious about taking the next letter as an argument, but I guess that's maybe a separate question...

\def\dothings#1{\color{blue}\expandafter\dothings@i#1 \@nil}
\def\dothings@i#1 #2\@nil{%

\dothings{Takes several words as argument}


enter image description here

  • I think this needs an extra set of {} to keep the color change local to \donothing. So the first \def should be \def\dothings#1{{\color{blue}\expandafter\dothings@i#1 \@nil}} – Peter Grill Aug 13 '11 at 17:54
  • The \expandafter is useless? – Leo Liu Aug 13 '11 at 18:19
  • @Leo Liu: I guess that Herbert wants to also cover the case where the sentence is given within a macro. – Bruno Le Floch Aug 14 '11 at 11:23

LaTeX3 now provides

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

which splits the <argument> at each occurrence of <delimiter> (and strips spaces from both ends of each argument), and puts the result in the <seq var> (the list data structure in LaTeX3). Once you have that sequence, you can map a given function on all its items.

\seq_new:N \l_foo_seq
    \seq_set_split:Nnn \l_foo_seq {~} {#1}
    \seq_map_inline:Nn \l_foo_seq { \fbox{##1} }
\dothings{Takes several words as argument}

EDIT: Actually you can do it faster with xparse's argument processors (here \SplitList).

\NewDocumentCommand {\dothings}
  { > { \SplitList { ~ } } m }
  { \tl_map_inline:nn {#1} { \fbox{##1} } }
\dothings{Takes several words as argument}
  • That's neat! I like that! – Seamus Aug 15 '11 at 9:48
  • When this goes onto CTAN, could you update this answer to reflect that? – Seamus Oct 1 '11 at 9:58
  • @Seamus Thanks for the reminder, it is now on CTAN. – Bruno Le Floch Oct 1 '11 at 18:03

Herbert has shown how to seperate words by spaces. Here is a very similar solution which prevent deep \ifx ... \fi nesting (Deep nesting may produce errors when many words is used):


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

\wordsloop{This is a sentence.}
% we have: (This) (is) (a) (sentence.)


However, it is still very slow if you use many words in the argument.

Loop on tokens can be obtained by similar approch, if you remove the spaces after #1 in the definitions above. But it can be easier:



\tokensloop{aa  bb}
% we have (a)(a)(b)(b)


You can also use \toksloop command from etextools package directly.

Note that spaces are ignored.

For spaces, catcode or other tricks may be used to read the characters verbatimly. That is more complex.

For example, we can use \obeyspaces to change the catcode of spaces:



\charsloop{aa  bb}
% we have (a)(a)( )( )(b)(b)


(Newlines are still ignored with \obeyspaces only.)


ConTeXt provides a macro \processisolatedwords that does exactly that. For example, to draw a frame around each word, you can use

\processisolatedwords {\input ward \relax} \inframed

In MkIV, this macro is implemented in lua using the following funcction:

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
                start = nil
        elseif nested and (id == hlist_code or id == vlist_code) then
        elseif not start then
            start = current
        current = current.next
    if start then

See also:

  • \applytowords
  • \processwords and \applytosplitstringword

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.