5

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?

Paulo Cereda
  • 44,220

3 Answers3

5

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}
Andrew Stacey
  • 153,724
  • 43
  • 389
  • 751
5

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} 
Alain Matthes
  • 95,075
3

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}
Leo Liu
  • 77,365
  • \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