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.

Friends, I used to suffer a lot when had to iterate through elements of a list, but thanks to egreg's suggestion, I've been successfully using the etoolbox package for that task, e.g.:

\DeclareListParser*{\dothedance}{;}
\dothedance{\fbox}{item1; item2; item3}

Now, I'm trying to get all the pairs of a cartesian product of two lists (pardon the following Haskell code):

Prelude> [ (x,y) | x <- ['A','B','C'], y <- ['a','b','c','d'] ]
[('A','a'),('A','b'),('A','c'),('A','d'),('B','a'),('B','b'),('B','c'),('B','d'),('C','a'),('C','b'),('C','c'),('C','d')]

Then, for each pair, I'd like to break it into two parameters (x and y) and pass them to a command.

For example, consider two lists [Hello, Howdy] and [Ann, Mary, Kate] and the following dummy command:

\newcommand\saysomething[2]{#1~#2}

I'd expect each pair to be the parameters of that command. So the output would be:

Hello Ann
Hello Mary
Hello Kate
Howdy Ann
Howdy Mary
Howdy Kate

I'm pretty sure etoolbox could help me on that, but all my attempts of iterating through two lists were unsuccessful. I'm probably missing something obvious, but I need enlightment. Any ideas?

share|improve this question

3 Answers 3

up vote 5 down vote accepted

This could be more elegant ...

\documentclass{article}
\usepackage{etoolbox}

\DeclareListParser*{\dothedance}{,}

\newcommand{\saysomething}[2]{#1~#2\par}
\newcommand{\sayitto}[1]{%
  \dothedance{\saysomething{#1}}{Ann, Mary, Kate}
}


\begin{document}
\dothedance{%
\sayitto
}{Hello, Howdy}
\end{document}

produces:

Hello Ann
Hello Mary
Hello Kate
Howdy Ann
Howdy Mary
Howdy Kate

(In response to comment) To make it so that both lists can be specified at call-time:

\documentclass{article}
\usepackage{etoolbox}

\DeclareListParser*{\dothedance}{,}
\DeclareListParser*{\dotheotherdance}{,}

\newcommand{\saysomething}[2]{#1~#2\par}
\newcommand{\sayitto}[1]{%
  \dothedance{\saysomething{#1}}
}

\newcommand{\dothetwostep}[3]{%
  \def\partner##1{%
    \dothedance{#1{##1}}{#3}%
  }
  \dothedance{\partner}{#2}
}


\begin{document}
\dothetwostep{\saysomething}{Hello, Howdy}{Ann, Mary, Kate}
\end{document}
share|improve this answer
    
It's a very smart approach, Andrew! Unfortunately, for my particular case, both lists tend to be mutable throughout the document. If one of my lists were immutable, then I had no doubt on using your approach, it's a very concise code! =) –  Paulo Cereda Jun 23 '11 at 12:11
    
@Paulo: That's easily solved. –  Loop Space Jun 23 '11 at 12:19
    
Absolutely brilliant, Andrew! Thanks a million! –  Paulo Cereda Jun 23 '11 at 12:28

Why not :

\documentclass{article}
\usepackage{pgffor}

\newcommand{\saysomething}[2]{ 
  \foreach \w in #1{%
  \foreach \n in #2{%
     \w~\n \par}}}  

\begin{document}
\def\Lone{ Hello, Howdy}
\def\Ltwo{ Ann, Mary, Kate}
\saysomething{\Lone}{\Ltwo}

\end{document} 
share|improve this answer
    
Wow, pretty impressive code! Thanks a lot for this alternative solution using pgffor, I was unaware of! Right now I need to stick with the etoolbox package because it's a collaborative document, but for the next documents, I'll give pgffor a try! –  Paulo Cereda Jun 23 '11 at 12:33
    
I second the recommendation to use \foreach here. –  Loop Space Jun 23 '11 at 12:39
    
I prefer this one. –  Leo Liu Jun 23 '11 at 13:00

This is another solution using etoolbox to follow Paulo Cereda's origin idea (direct nested loop).

And I add a \directprodM to allow the csv lists to be macro names.

\documentclass{article}
\usepackage{etoolbox}

% #1 is a function
% #2 and #3 are literal csv lists
\newcommand\directprod[3]{%
  \renewcommand\do[1]{%
    \begingroup
    \renewcommand\do[1]{%
      #1{##1}{####1}}% awful, indeed
    \docsvlist{#3}%
    \endgroup}%
  \docsvlist{#2}}

% #2 and #3 are macros
\newcommand\directprodM[3]{%
  \edef\next{\noexpand\directprod\noexpand#1{#2}{#3}}%
  \next}

\begin{document}

\newcommand\saysomething[2]{#1~#2\par}

\directprod\saysomething{Hello, Howdy}{Ann, Mary, Kate}

or using macros for lists:
\newcommand\listA{Hello, Howdy}
\newcommand\listB{Ann, Mary, Kate}
\directprodM\saysomething\listA\listB

\end{document}
share|improve this answer
    
\directprod here is neither shorter nor simpler than @Andrew's definition. It just show how to nest macro definitions correctly. And supply a \directprodM to complete Andrew's solution. –  Leo Liu Jun 23 '11 at 12:58
    
Fantastic, Leo. I was struggling with those nested definitions and you made them clearer! Thanks! –  Paulo Cereda Jun 23 '11 at 13:44

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.