# A macro that passes each word of its argument to another macro as an argument?

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...

-
I just found this related question: tex.stackexchange.com/questions/11595/… – Seamus Aug 29 '11 at 10:40

## 4 Answers

\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}


-
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.

\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}


EDIT: Actually you can do it faster with xparse's argument processors (here \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}

-
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):

\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}


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:

\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}


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:

\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}


(Newlines are still ignored with \obeyspaces only.)

-
Is it simple to modify this to separate based on &. I am still searching for a more general solution to macro to allow for alignment points within its argument – Peter Grill Aug 13 '11 at 18:29

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
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


See also:

• \applytowords
• \processwords and \applytosplitstringword
-