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.

I want to cycle over several sentences using a foreach loop. Now sentences will prpbably contain commas, which are used by foreach as item separators. So I would like to change the item separator to something that does not appear in my sentences, like e.g. |

So instead of

\foreach \x in {sentenceA,sentenceB,sentenceC}

I would like to use

\foreach \x in {sentenceA|sentenceB|sentenceC}

Is this possible?

share|improve this question
2  
How about putting your sentences in braces? –  Loop Space Sep 19 '12 at 10:42
    
I thought about that, but that wouldn't be easy to automate, as sentences may also contain dots (like in e.g.) where I don't want a separation. Manually putting .| at the end of a sentence will be much easier (or !| or ?|) –  Tom Bombadil Sep 19 '12 at 10:53

6 Answers 6

up vote 9 down vote accepted

Here's an expl3 implementation:

\documentclass{article}
\usepackage[T1]{fontenc}
\usepackage{xparse,pgffor}
\ExplSyntaxOn

\NewDocumentCommand{\Foreach}{ m O{,} m}
 {
  \__foreach_main:nn { #1 } { #2 } { #3 }
 }

\seq_new:N \l__foreach_arg_seq
\clist_new:N \l__foreach_braced_clist

\cs_new_protected:Npn \__foreach_main:nn #1 #2 #3
 {
  \seq_set_split:Nnn \l__foreach_arg_seq { #2 } { #3 }
  \clist_clear:N \l__foreach_braced_clist
  \seq_map_inline:Nn \l__foreach_arg_seq
   {
    \clist_put_right:Nn \l__foreach_braced_clist { { ##1 } }
   }
  \foreach #1 in~\l__foreach_braced_clist
 }
\ExplSyntaxOff

\begin{document}

\Foreach{\x}[|]{sentenceA|se,nt,en,ce,B| sentenceC }{[\x]}

\Foreach{\x}{sentenceA|se,nt,en,ce,B| sentenceC }{[\x]}

\end{document}

The syntax is a bit different than \foreach \x in {...}, but this shouldn't be a big problem. The default separator is the comma, a different one can be specified in the optional argument after the variable.

enter image description here

share|improve this answer
    
How to use this Foreach command in the case of \foreach [some options] \x in . Should I add an optional <> parameter for the loop options ? –  Tarass Apr 28 at 19:37
    
@Tarass I don't know well the options to \foreach. –  egreg Apr 28 at 21:28
    
See my proposition below. The new command does not use foreach itself but prepare the string to be foreach compatible, then you can freely use all foreach options. –  Tarass Apr 29 at 4:40

The comma is hardcoded in pgffor's \foreach. You could convert the list before and store it in a macro. The following example puts the entries in curly braces to protect the commas. Then initial and final spaces are also kept. In the example only an initial space is removed.

\documentclass{article}
\usepackage{pgffor}

\makeatletter
\newcommand{\tocommalist}[3]{%
  \begingroup
    \toks@{}
    \let\@tmp\relax
    \long\def\@tocommalist##1#2##2\@nil{%
      \ifx\@tmp\relax
        % \toks@{{##1}}% without initial trimming
        \toks@\expandafter{\expandafter{\romannumeral-`\x##1}}%
      \else
        % \toks@\expandafter{\the\toks@,{##1}}% without initial trimming
        \toks@\expandafter{%
          \the\expandafter\toks@\expandafter,%
          \expandafter{\romannumeral-`\x##1}%
        }%
      \fi
      \def\@tmp{##2}%
      \ifx\@tmp\@empty
        \expandafter\@gobbletwo
      \fi
      \@tocommalist##2\@nil
    }%
    \@tocommalist#3#2\@nil
  \expandafter\endgroup
  \expandafter\def\expandafter#1\expandafter{\the\toks@}%
}
\makeatother

\tocommalist\mylist{|}{sentenceA|se,nt,en,ce,B| sentenceC }
\foreach \x in \mylist {%
  \typeout{[\meaning\x]}%
}

\begin{document}
\foreach \x in \mylist {%
  [\x]\par
}
\end{document}

Result


Another example using package etoolbox. It also only trims the beginning of the entry and it removed empty entries:

\documentclass{article}

\usepackage{pgffor}
\usepackage{etoolbox}
\usepackage{etexcmds}
\DeclareListParser*{\forvertlist}{|}

\makeatletter
\newcommand{\vertocomma}[2]{%
  \let#1\@empty
  \forvertlist{\@verttocomma{#1}}{#2}%
}
\newcommand{\@verttocomma}[2]{%
  \edef#1{%
    \ifx#1\@empty
    \else
      \etex@unexpanded\expandafter{#1},%
    \fi
    {\etex@unexpanded{#2}}%
  }%
}
\makeatother

\vertocomma\mylist{sentenceA|| |se,nt,en,ce,B| sentenceC |}
\foreach \x in \mylist {%
  \typeout{[\meaning\x]}%
}

\begin{document}
\foreach \x in \mylist {%
  [\x]\par
}
\end{document}

Result

share|improve this answer

uses parentheses { aaa,nnn!} to separate items

\documentclass{article}
\usepackage{tikz}

\begin{document}

\begin{tikzpicture}

\foreach \nn in{{0,1},{1,0},{0,-1},{-1,0}}{
\draw (0,0) -- (\nn);
}
\end{tikzpicture}

\end{document}

enter image description here

share|improve this answer
1  
I know that it can be done like this, but that's just what I wanted to avoid (see Andrew Stacey's comment on my question). –  Tom Bombadil Sep 19 '12 at 11:34

When no auto-list-completion is required, catoptions package's \indrisloop can handle any list separator. The \newforeach macro can naturally accept any type of list separator and it can do list completion, but it is not yet on CTAN. Here is a \indrisloop solution. This should work even when the list separator was active. As you will notice in the result of the example, spurious spaces between the elements of the list are removed.

\documentclass{article}
\usepackage{catoptions}

\begin{document}

\def\bombadil#1{%
  \typeout{Doing item: #1}%
  \edef\transformedlist{%
    \ifdefFT\transformedlist{}{\expandcsonce\transformedlist}%
    \unexpanded{\\{#1}}%
  }%
}

% \indrisloop[<separator>]{<list>}{<callback>}

\indrisloop[|]{%
  David Carlisle, item 1 |
  Heiko Oberdiek, item 2 |
  David Salomon, item 3 |
  Frank Mittelbach, item 4 |
  Donald Arseneau; item 5
}\bombadil

\show\transformedlist
\end{document}

{\show}
> \transformedlist=macro:

\\{David Carlisle, item 1}\\{Heiko Oberdiek, item 2}\\{David Salomon, item 3}
\\{Frank Mittelbach, item 4}\\{Donald Arseneau; item 5}.

l.21 \show\transformedlist

D.E. Knuth liked to use \\ as a list separator.

You can transform an arbitrary list into a comma-separated list using the following \makeintocommalist.

% \makeintocommalist{<original.parser>}{<orig.list.cmd>}{<transformed.list.cmd>}

\def\makeintocommalist#1#2#3{%
  \def#3{}%
  \def\bombadil##1{%
    \edef#3{%
      \expandcsonce#3{\unexpanded{##1}}%
      \iflastindris\else,\fi
    }%
  }%
  \indrisloop*[#1]{#2}\bombadil
}

Example:

\def\alist{%
  David Carlisle, item 1 |
  Heiko Oberdiek, item 2 |
  David Salomon, item 3 |
  Frank Mittelbach, item 4 |
  Donald Arseneau; item 5 |
  Enrico Gregorio/another great |
  Ulrich Diez, yes! |
  Joseph Wright, also/+
}

\makeintocommalist{|}\alist\blist
\show\blist

> \blist=macro:->

{David Carlisle, item 1},{Heiko Oberdiek, item 2},{David Salomon, item 3},
{Frank Mittelbach, item 4},{Donald Arseneau; item 5},{Enrico Gregorio/another great},
{Ulrich Diez, yes!},{Joseph Wright, also/+}.

l.53 \show\blist

EDIT

Now I remember that the catoptions package has a \cptforeach command that does exactly what you wanted. But it doesn't do auto-completion of lists. See it in action below. It is happy with arbitrary (even weird) list item separators.

% \cptforeach[<optional.sep>] <holders> \in <list> \do {<callback>}

% Note the list separators in these loops:

\tikz[shading=ball]
  \cptforeach[:] \x+\cola \in 0+red : 1+green : 2+blue : 3+yellow \do {%
    \cptforeach[|] \y;\colb \in {0;red | 1;green | 2;blue | 3;yellow} \do {%
      \shade[ball color=\cola!50!\colb] (\x,\y) circle (0.4cm);
  }%
};

enter image description here

The above is equivalent to

\tikz[shading=ball]
  \cptforeach \x/\cola \in 0/red, 1/green, 2/blue, 3/yellow \do {%
    \cptforeach \y/\colb \in {0/red, 1/green, 2/blue, 3/yellow} \do {%
      \shade[ball color=\cola!50!\colb] (\x,\y) circle (0.4cm);
  }%
};
share|improve this answer

Full foreach options compatible solution.

The idea is to prepare the loop-string to be foreach compatible ie to substitute the significative comma by a phantom caractere before replace the alternative separator by the foreach-needed comma. Then in the foreach loop itself, to remplace the eventually phantom caractere by a significative comma.

enter image description here

\documentclass[french]{article}
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\usepackage{babel}          % compatibilité avec Babel
\usepackage{babel}
\usepackage{xparse,xstring,tikz}
%\usetikzlibrary{}

\NewDocumentCommand{\FENewList}{
    D<>{\&}     % phantom substitution caractere
    O{;}        % alternative separator
    m           % input list
    O{\FEList}  % output list
    }{%
    \StrSubstitute{#3}{,}{#1}[#4]
    \StrSubstitute{#4}{#2}{,}[#4]
    }

\NewDocumentCommand{\FERestore}{
    D<>{\&}     % phantom substitution caractere
    m           % input string
    }{%
    \StrSubstitute{#2}{#1}{,}[#2]%
    }

\begin{document}

With for \texttt{;} as alternative separator:

\FENewList{sentenceA;se,nt,en,ce,B; sentenceC }
\foreach \x in \FEList {%
\FERestore{\x}
[\x]}

\medskip
With for \texttt{|} as alternative separator:

\FENewList[|]{sentenceA|se,nt,en,ce,B| sentenceC }
\foreach \x in \FEList {%
\FERestore{\x}
[\x]}

\medskip
With for \texttt{foreach} options:

\FENewList[|]{sentenceA|se,nt,en,ce,B| sentenceC }
\foreach \x [count=\i from 1] in \FEList {%
\FERestore{\x}
\noindent\i: [\x]\\}

\FENewList[|]{3|...|5}
\foreach \x in \FEList {%
\FERestore{\x}
\x }

\begin{tikzpicture}

\FENewList{(0,3);(1,4);(2,3);(3,1)}
\foreach \p in \FEList 
{
\FERestore{\p}
\fill \p circle [radius=.1] ;
} 

\end{tikzpicture}
share|improve this answer

A little late to the game, but a short and simple hack is sufficient to include both the ... notation and coordinate parsing:

\documentclass[border=5]{standalone}
\usepackage{tikz}
\makeatletter
\let\pgffor@dotsscanend@original=\pgffor@dotsscanend
\def\pgffor@setdelimiter#1{%
  \def\pgffor@emptyvalues{#1 \pgffor@stop#1}%
  \def\pgffor@normal@list##1{%
    \def\pgffor@values{##1#1 \pgffor@stop#1}%
    \ifx\pgffor@values\pgffor@emptyvalues
      \def\pgffor@values{\pgffor@stop#1}%
    \fi%
    \let\pgffor@body=\pgfutil@empty%
    \global\pgffor@continuetrue%
    \pgffor@collectbody}%
  \def\pgffor@scanround(##1)##2#1{\def\pgffor@value{(##1)##2}\pgffor@scanned}%
  \def\pgffor@scanone##1#1{\def\pgffor@value{##1}\pgffor@scanned}%
  \def\pgffor@dotsscanend##1#1{\pgffor@dotsscanend@original##1,}%
}

\pgfkeys{/pgf/foreach/delimiter/.code=\pgffor@setdelimiter{#1}}
\begin{document}
\begin{minipage}{4in} \tt
\foreach \i in {the,{quick, brown},fox,jumps,over,the,lazy,dog}{[\i]}
\\\color{red}
\foreach \i [delimiter=*] in {the*quick, brown*fox*jumps*over*the*lazy*dog}{[\i]}
\\\color{blue}
\foreach \i [delimiter=|] in {the|quick, brown|fox|jumps|over|the|lazy|dog}{[\i]}
\\\color{green}
\foreach \i [delimiter=+] in {1+...+15}{[\i]}
\\\color{purple}
\foreach \i [delimiter=!] in {(1,0)!(2,1)!(3,1)!(5,4)!(0,1)!(8,2)!(4,3)}{[\i]}
\end{minipage}
\end{document}

enter image description here

share|improve this answer
    
I understand ... but what do you mean by "coordinate parsing" ? Could you give an example ? –  Tarass Apr 29 at 12:14
    
@Tarass \tikz\foreach \p in {(0,3),(1,4),(2,3),(3,1)}\fill \p circle [radius=.1]; –  Mark Wibrow Apr 29 at 13:39
    
Thank you, both work find with my proposition. –  Tarass Apr 29 at 14:17

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.