[Tex/LaTex] Define macro for sequence/list/tuple macros

expansionmacros

I would like to define a macro for defining further macros to display sequences/lists/tuples of elements by specifying the following parameters:

  1. The name of the new command.
  2. What to display for an empty sequence/list/tuple.
  3. How to open a singleton sequence/list/tuple.
  4. How to close a singleton sequence/list/tuple.
  5. How to open a sequence/list/tuple with more than one element.
  6. How to close a sequence/list/tuple with more than one element.
  7. What to display as separating object between two elements in the sequence/list/tuple.

I had a look at the following questions and corresponding answers:

So I tried to code my command and came up with the following two versions (the second one is outcommented in the code below). Unfortunately, none of them works and I cannot figure out what the problem is:

\documentclass{article}

\makeatletter 
\newcommand{\definelistcommand}[7]{%
\expandafter\newcommand\csname @start#1\endcsname{\expandafter\@ifnextchar\csname @stop#1\endcsname{#2\csname @end#1\endcsname}{\csname @first#1\endcsname}}%
\expandafter\newcommand\csname @first#1\endcsname[1]{\expandafter\@ifnextchar\csname @stop#1\endcsname{#3##1#4\csname @end#1\endcsname}{#5##1\csname @next#1\endcsname}}%
\expandafter\newcommand\csname @next#1\endcsname[1]{#7##1\expandafter\@ifnextchar\csname @stop#1\endcsname{#6\csname @end#1\endcsname}{\csname @next#1\endcsname}}%
\expandafter\newcommand\csname @end#1\endcsname[1]{}% consumes the \stop command
\expandafter\newcommand\csname #1\endcsname[1]{\csname @start#1\endcsname##1\csname @stop#1\endcsname}%
}
\makeatother

% \makeatletter 
% \edef\definelistcommand#1#2#3#4#5#6#7{%
% \noexpand\newcommand\expandafter\noexpand\csname @start#1\endcsname{\noexpand\@ifnextchar\expandafter\noexpand\csname @stop#1\endcsname{#2\expandafter\noexpand\csname @end#1\endcsname}{\expandafter\noexpand\csname @first#1\endcsname}}%
% \noexpand\newcommand\expandafter\noexpand\csname @first#1\endcsname[1]{\noexpand\@ifnextchar\expandafter\noexpand\csname @stop#1\endcsname{#3##1#4\expandafter\noexpand\csname @end#1\endcsname}{#5##1\expandafter\noexpand\csname @next#1\endcsname}}%
% \noexpand\newcommand\expandafter\noexpand\csname @next#1\endcsname[1]{#7##1\noexpand\@ifnextchar\expandafter\noexpand\csname @stop#1\endcsname{#6\expandafter\noexpand\csname @end#1\endcsname}{\expandafter\noexpand\csname @next#1\endcsname}}%
% \noexpand\newcommand\expandafter\noexpand\csname @end#1\endcsname[1]{}% consumes the \stop command
% \noexpand\newcommand\expandafter\noexpand\csname #1\endcsname[1]{\expandafter\noexpand\csname @start#1\endcsname##1\expandafter\noexpand\csname @stop#1\endcsname}%
% }
% \makeatother

\definelistcommand{mylist}{empty}{[}{]}{(}{)}{,}

\begin{document}

\mylist{{a}{b}{c}}

\mylist{{a}{b}}

\mylist{a}

\mylist{{b}}

\mylist{}

\end{document}

The desired output would be:

(a,b,c)
(a,b)
[a]
[b]
empty

For the first version, I get this error message:

! Missing \endcsname inserted.
<to be read again>
                   \let
l.27 \mylist{{a}{b}{c}}

For the second version, I get this:

! Undefined control sequence.
l.27 \mylist
            {{a}{b}{c}}

What are the errors in my commands and how would a correct solution work?

EDIT (to point future readers to the fact that the accepted answer is not the only interesting one):
Although I accepted the answer by Christian Hupfer (since it contains an explanation and fix for my attempt), the other answers also contain valuable contributions. In particular, I will use egreg's solution as it allows a key-value pair specification of my desired parameters. Unfortunately, I cannot accept two answers.

Best Answer

Here's a expl3 version with seq variables.

The command \NewListCommand declares the command list macro named like the first argument and uses a global seq variable with some prefix, see \g_cryingshadow_mynewlist_seq etc.

This is filled by the \mynewlist command and the content displayed depending on the content of the other parameters from #3 to #6. The 7th. parameter is used as a delimiter

Some notes: The empty parameter could be an optional one.

\documentclass{article}


\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\NewListCommand}{mmmmmmm}{%
  \seq_new:c {g_cryingshadow_#1_seq} % Make a new sequence
  \expandafter\NewDocumentCommand\csname #1\endcsname{m}{%
    \seq_gclear:c {g_cryingshadow_#1_seq}% Clear the sequence
    \seq_gclear:N \l_tmpa_seq % Clear a temporary `\l_tmpa_seq
    \seq_set_split:Nnn \l_tmpa_seq {} {##1} % Split the sequence into tokens. 
    \seq_set_eq:cN {g_cryingshadow_#1_seq} \l_tmpa_seq % copy the temporary sequence to the real one → this would not be necessary if the sequence is not be used outside 
    \seq_if_empty:cTF {g_cryingshadow_#1_seq} {%
      #2% Checked --> is empty
    }{%
      % Display the various styles
      \int_case:nn { \seq_count:c {g_cryingshadow_#1_seq} }
      {%
        {1} {#3      \seq_use:cnnn {g_cryingshadow_#1_seq} {,} {#7} {#7} #4}
      }%
      \int_compare:nNnT { \seq_count:c {g_cryingshadow_#1_seq}}  > 1 {%
        #5      \seq_use:cnnn {g_cryingshadow_#1_seq} {#7} {#7} {#7} #6
      }
    }
  }
}
\ExplSyntaxOff


\NewListCommand{mynewlist}{empty}{[}{]}{(}{)}{,}


\begin{document}

\mynewlist{{A}{B}{C}}

\mynewlist{{A}{B}}

\mynewlist{{a}}

\mynewlist{}


\mynewlist{{A}{B}{C}{D}{E}{F}}


Astronomer's alphabet:

\mynewlist{{Oh}{be}{a}{fine}{girl}{kiss}{me}}

\end{document}

enter image description here

Edit: Here's the working version for usual LaTeX:

\documentclass{article}
\usepackage{etoolbox}

\makeatletter

\def\definelistcommand#1#2#3#4#5#6#7{%

  \expandafter\def\csname @firstinlist#1\endcsname##1{%
    \@ifnextchar\@stoplist{%
      #3##1#4\@endlist%
    }{%
      #5##1\csname @nextlist#1\endcsname%
    }%
  }

  \expandafter\def\csname @nextlist#1\endcsname##1{%
    \@ifnextchar\@stoplist{%
      #7##1#6\@endlist%
    }{%
      #7##1\csname @nextlist#1\endcsname%
    }%
  }


  \expandafter\def\csname @startlist#1\endcsname##1{%
    \@ifnextchar\@stoplist{%
      #3##1#4\@endlist%
    }{%
      \csname @firstinlist#1\endcsname##1% 
    }%
  }

  \expandafter\def\csname #1\endcsname##1{%
    \ifblank{##1}{%
      #2%
    }{%
      \csname @startlist#1\endcsname##1\@stoplist%
    }%
  }
}

\def\@endlist#1{}


\definelistcommand{mycmd}{empty}{[}{]}{(}{)}{,}

\definelistcommand{othercmd}{empty}{\textbraceleft}{\textbraceright}{(}{)}{+}

\begin{document}


\makeatother

\mycmd{{A}{B}{C}}

\mycmd{A}

\mycmd{{A}{B}}

\mycmd{}

\othercmd{{O}{B}}

\othercmd{{O}}


\end{document}
Related Question