Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

It seems that a common operation needed when defining new commands is to scan over a list of arguments (separated e.g. by a comma, but maybe by something else) and do something with each argument in the list, find the last one, or whatever.

For example to write commands that look like \Command{a,b,c} or \Command{c.d.e} and do something with each a, b, and c. See e.g. questions on defining a list of operators and ignoring even numbered inputs.

Some solutions have been posted there, but what would be the best idiom to perform such kind of operations? LaTeX's \@for looks very nice, but is it possible to use it to scan anything other than comma-separated lists?

share|improve this question
add comment

8 Answers 8

up vote 18 down vote accepted

I have not progressed to the level of processing a list myself but I have been using etoolbox by Philipp Lehman for some other TeX programming tasks I am doing. I believe 3.7 List processing should give you a good solution.

For example (on a recent version of etoolbox) you can write:

\DeclareListParser*{\myfor}{;}
\myfor{\fbox}{item1; item2; item3}
share|improve this answer
1  
Thanks for the reference! It does have some nice commands to process a list of arguments. If you don't mind I can edit your answer to include an example. –  Juan A. Navarro Aug 6 '10 at 20:53
    
Please go ahead. –  Leo Liu Aug 6 '10 at 21:44
add comment

I was hoping someone to post a better/nicer solution, but so far this seems to be the best one I've seen.

\def\do@scan#1:{%
  \ifx#1\relax
    \let\next\relax
  \else
    \DoSomethingWith{#1}\let\next\do@scan
  \fi\next
}
\newcommand{\scanlist}[1]{\do@scan#1:\relax:}

Of course instead of : one can use , or any other sensible separator.

share|improve this answer
    
I agree that this is a very good solution, since it does not require any other package at all and needs very few lines of code nevertheless. –  Rupert Jones Mar 25 '11 at 14:32
add comment

Here is an LPEG based solution. This is written in ConTeXt, but should be easy to modify for LuaLaTeX

\unprotected\def\ProcessListWithDelim#1[#2]#3% delim list command
  {\ctxlua{userdata.ProcessListWithDelim(\!!bs#1\!!es, \!!bs#2\!!es, \!!bs\noexpand#3\!!es)}} 

\startluacode
  userdata = userdata or {}

  function userdata.ProcessListWithDelim(delim, list, command)
    local splitter = lpeg.Ct(lpeg.splitat(lpeg.P(delim)))
    local items    = lpeg.match(splitter, list)

    for i=1,#items do
      tex.sprint(tex.ctxcatcodes, command .. '{' .. items[i] .. '}')
    end
  end
\stopluacode

\def\ShowItem#1{>>>#1<<<\crlf}

\def\CommandA#1{\ProcessListWithDelim,[#1]\ShowItem}

\def\CommandB#1{\ProcessListWithDelim{:}[#1]\ShowItem}

\starttext
\CommandA{A,Comma,Separated,List}


\CommandB{A:Colon:Separated:List}
\stoptext
share|improve this answer
add comment

In ConTeXt, list processing is done using \processcommalist. Suppose you want to apply the macro \doCommand over each list element passed to \Command. The following definition of \Command does this:

\def\Command#1{\processcommalist[#1]\doCommand}

If the list may contain macros, like

\def\Arguments{a,b,c}
\Command{\Arguments,d,e,f}

then you can use \processcommacommand, like

\def\Command#1{\processcommalistcommand[#1]\doCommand}

There are a some faster alternatives, \rawprocesscommalist and \rawprocesscommacommand but they have certain limitations. See the ConTeXt wiki page on list processing for details.

To process a list with an arbitrary separator, \processlist may be used. \processlist takes 5 arguments: beginning of list marker, end of list marker, element separator, command to apply to each element, and the list. For example, a command \Command[a:b:c] that uses colon as the separator, can be defined as

\def\Command{\processlist[]:}
share|improve this answer
    
but what about lists separated by a character other than a comma? –  Juan A. Navarro Oct 14 '10 at 14:58
    
\processlist can be used to process a list with a particular separator. I added that information in the answer. –  Aditya Oct 21 '10 at 1:11
    
Nice, thank you! –  Juan A. Navarro Oct 21 '10 at 9:02
add comment

Here's a complete example using the \docsvlist command provided by etoolbox. The example introduces a package called csvfont and its usecsvfont environment. The font and formatting of this environment can be set using the package/environment options font (which resets the font definition) and font+ (which appends the font definition). Both options may take a comma-separated list of values; as individual values one may specify the names (without preceding backslash) of LaTeX's font-switching/formatting commands. (This syntax is used in the upcoming version 0.1b of my quoting package.)

\RequirePackage{filecontents}

\begin{filecontents}{csvfont.sty}
\ProvidesPackage{csvfont}

\RequirePackage{etoolbox,kvoptions}
\SetupKeyvalOptions{family=csf,prefix=csf@}

\newcommand*{\csvfont}{}

\define@key{csf}{font}{%
  \def\csvfont{}%
  \renewcommand*{\do}[1]{\appto{\csvfont}{\csname ##1\endcsname}}%
  \docsvlist{#1}%
}
\define@key{csf}{font+}{%
  \renewcommand*{\do}[1]{\appto{\csvfont}{\csname ##1\endcsname}}%
  \docsvlist{#1}%
}

\ProcessKeyvalOptions*

\newenvironment{usecsvfont}[1][]{\setkeys{csf}{#1}\csvfont}{}

\endinput
\end{filecontents}

\documentclass{article}

\usepackage[font={small,itshape}]{csvfont}

\usepackage{lipsum}

\begin{document}

\begin{usecsvfont}
\lipsum[1]
\end{usecsvfont}

\begin{usecsvfont}[font+={bfseries,raggedright}]
\lipsum[1]
\end{usecsvfont}

\begin{usecsvfont}[font={bfseries,raggedright}]
\lipsum[1]
\end{usecsvfont}

\end{document}

enter image description here

share|improve this answer
    
...that pushed you over the 50K edge. Glad I could be the one. :) –  Werner Feb 24 '12 at 18:35
add comment

Since Morbusg's answer pushed that question to the top, let me add a LaTeX3 answer. The idea is to convert the list to an internal structure called a sequence, and then map over that sequence \l_MyMap_seq.

\documentclass{article}
\usepackage{expl3, xparse}
\ExplSyntaxOn
\seq_new:N \l_MyMap_seq
\NewDocumentCommand{\MyMap}{O{,}mm}
  {
    \seq_set_split:Nnn \l_MyMap_seq {#1} {#2}
    \seq_map_inline:Nn \l_MyMap_seq {#3}
  }
\ExplSyntaxOff
\begin{document}
  \MyMap{a,b,c}{\fbox{#1} }
  \begin{tabular}{c}
    \MyMap[:]{a:b:c}{#1\\}
  \end{tabular}
\end{document}

The first (optional) argument is the delimiter, by default a comma. The second argument is the list, and the third is a function, taking each item of the list as #1.

The xparse package also provides \SplitList, which can be used as

\NewDocumentCommand {\foo} { >{\SplitList{.}} m m } { \tl_map_inline:nn {#1}{#2} }

hard-coding the delimiter in the definition of \foo.

share|improve this answer
add comment

Here's my solution (borrowed form Kevin Walker):

% tricky way to iterate macros over a list
\def\semicolon{;}
\def\applytolist#1{
    \expandafter\def\csname multi#1\endcsname##1{
        \def\multiack{##1}\ifx\multiack\semicolon
            \def\next{\relax}
        \else
            \csname #1\endcsname{##1}
            \def\next{\csname multi#1\endcsname}
        \fi
        \next}
    \csname multi#1\endcsname}

And some examples of its use:

% \def\cA{{\cal A}} for A..Z
\def\calc#1{\expandafter\def\csname c#1\endcsname{{\mathcal #1}}}
\applytolist{calc}QWERTYUIOPLKJHGFDSAZXCVBNM;

% \DeclareMathOperator{\pr}{pr} etc.
\def\declaremathop#1{\expandafter\DeclareMathOperator\csname #1\endcsname{#1}}
\applytolist{declaremathop}{pr}{im}{gl}{ev}{coinv}{tr}{rot}{Eq}{obj}{mor}{ob}{Rep}{Tet}{cat}{Maps}{Diff}{Homeo}{sign}{supp}{Nbd}{res}{rad};
share|improve this answer
    
This is very similar to the proposed solutions posted in the original threads (see the question), I wanted to know if there is anything better. Moreover, I would like to process lists of arguments given as \Command{a,b,c} or \Command{a:b:c}. –  Juan A. Navarro Jul 27 '10 at 16:39
add comment

Plain example of “Lists in TeX's mouth”, lambda.sty (TUGboat article):

\catcode`@=11
\input lambda.sty
\catcode`@=12
\def\csnameafter#1#2{\expandafter#1\csname#2\endcsname}
\def\MathOp#1{\csnameafter\def{#1}{\mathop{\it #1}}%
  #1} % show it, too
\def\Odd#1{\TeXif{\ifodd#1 }}

\Show\Filter\Odd[1,2,3,4,5,6,7,8,9,10,11,12,13] % => [1,3,5,7,9,11,13]

\Show\Map\MathOp[Rep,Tet,Maps,Diff] $ \Rep\Tet\Maps\Diff $
\bye
share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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