# How to make a recursive Latex macro with comma separated argument list

I would like to make a macro that takes two arguments, the second of which is a comma separated list and then produces a nested commutator of argument1 with each entry of argument2. I've been attempting to do this by looping over the values in argument2 and recursively calling a simple commutator macro, but I keep getting the following error:

! TeX capacity exceeded, sorry [grouping levels=255].


I tried to use etoolbox's docsvlist, but couldn't figure out how to make it do what I wanted.

It seems like I have an infinite loop, or some problem with my def scheme, but I don't know what it is.

Details:
Basic commutator macro:

\newcommand{\commut}[2]{\left[{#1},{#2}\right]}


Brute force nested commutator:

\newcommand{\qcommut}[5]{\commut{\commut{\commut{\commut{#1}{#2}}{#3}}{#4}}{#5}}


Attempt at automated version:

\makeatletter
\newcommand{\ncommut}[2]{
\def\mainArg{#1}
\newcommand{\argList}{#2}
\@for\i:=\argList\do{
\def\mainArg{
\commut{\mainArg}{\i}
}
}
}
\makeatother


I can call \commut and \qcommut, but \ncommut produces the error.

$$\commut{A}{B} \qcommut{A}{B}{C}{D}{E} \ncommut{A}{B,C,D,E} % desired usage$$


Desired output:

-
So could you include (for the sake of completeness), the expected output from using \commut{A}{B}, \qcommut{A}{B}{C}{D}{E} and (say) \ncommut{A}{B,C,D,E}? –  Werner Dec 4 '12 at 20:40
You can't use \mainArg in the definition of \mainArg. You need to expand it first. –  cgnieder Dec 4 '12 at 20:41
Note that all 3 answers (and latex syntax guidelines) have followed your question title and use comma separated list, but your example code used multiple {} groups (which has no obvious terminator for the list) –  David Carlisle Dec 4 '12 at 21:10

Here's a different LaTeX3 implementation that allows also for nested calls:

\documentclass{article}
\usepackage{xparse,mleftright}
\ExplSyntaxOn
\NewDocumentCommand{\commut}{ m }
{
\group_begin: \mactay_commut:n { #1 } \group_end:
}
\cs_new_protected:Npn \mactay_commut:n #1
{
\seq_set_from_clist:Nn \l__mactay_list_seq { #1 }
\int_compare:nTF { \seq_count:N \l__mactay_list_seq = 1 }
{ #1 } % only one element
{ % more than one element
% detach the first element
\seq_pop_left:NN \l__mactay_list_seq \l__mactay_first_tl
% add as many \mleft[ as needed
\prg_replicate:nn { \seq_count:N \l__mactay_list_seq }
{ \mleft[ }
% put the first element and a comma
\tl_use:N \l__mactay_first_tl ,
% deliver all the other elements, followed by \mright] and a comma
\seq_use:Nnnn \l__mactay_list_seq { \mright] , } { \mright] , } { \mright] , }
% final \mright]
\mright]
}
}
\seq_new:N \l__mactay_list_seq
\tl_new:N \l__mactay_first_tl
\ExplSyntaxOff

\begin{document}

$\commut{A}$

$\commut{A,B}$

$\commut{A,B,C}$

$\commut{A,B,C,D}$

$\commut{\commut{A,B},\commut{C,D}}$

\end{document}


-

\documentclass{article}
\newcommand{\commut}[2]{\left[{#1},{#2}\right]}

\makeatletter
\def\qcommut#1{\xcommut#1,\relax,}
\def\xcommut#1,{\xxcommut{#1}}
\def\xxcommut#1#2,{%
\ifx\relax#2%
#1%
\expandafter\@gobbletwo
\fi
\xxcommut{\commut{#1}{#2}}}

\begin{document}

$\qcommut{1,2,3,4,5,6}$
\end{document}

-

And a solution with kvsetkeys for parsing the comma separated list. \qcommut defines \commut@list containing the nested \commut calls with unexpanded arguments:

\documentclass{article}
\usepackage{kvsetkeys}
\usepackage{etexcmds}

\makeatletter
\newcommand*{\commut}[2]{\left[{#1},{#2}\right]}
\newcommand*{\qcommut}[1]{%
\let\commut@list\relax
\comma@parse{#1}\@qcommut
\typeout{\meaning\commut@list}% for debugging
\commut@list
}
\def\@qcommut#1{%
\ifx\commut@list\relax
\def\commut@list{#1}%
\else
\edef\commut@list{%
\noexpand\commut{%
\etex@unexpanded\expandafter{\commut@list}%
}{#1}%
}%
\fi
}
\makeatother

\begin{document}
$\qcommut{A,B,C,D,E}$
\end{document}


Macro \comma@list:

macro:->\commut {\commut {\commut {\commut {A}{B}}{C}}{D}}{E}


-

Here's a quick expl3 solution (provided that I understand correctly).

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn

\NewDocumentCommand {\ncommut} { m m }
{
\n_commut:nn {#1}{#2}
}

\cs_new:Npn \n_commut:nn #1#2
{
\tl_set:Nn \l_tmpa_tl {#1}
\clist_map_inline:nn {#2}
{
\tl_set:Nx \l_tmpa_tl {\__n_commut_aux:Vn \l_tmpa_tl {##1}}
}
\tl_use:N \l_tmpa_tl
}

\cs_new:Npn \__n_commut_aux:Vn #1#2
{
\left[{#1},{#2}\right]
}

\ExplSyntaxOff
\begin{document}

$\ncommut{1}{2,3,4,5}$

$\ncommut{a}{b,c,d,e,f,g}$

\end{document}

-