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 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.

\begin{equation}
  \commut{A}{B}
  \qcommut{A}{B}{C}{D}{E}
  \ncommut{A}{B,C,D,E} % desired usage
\end{equation}

Desired output:

Desired Output

share|improve this question
1  
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
1  
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

4 Answers 4

up vote 7 down vote accepted

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}

enter image description here

share|improve this answer

enter image description here

\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}
share|improve this answer

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}

Result

share|improve this answer

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

enter image description here

\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}
share|improve this answer

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.