# Define macro for sequence/list/tuple macros

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.

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

• Wouldn't this be much easier with etoolbox \listadd etc. or with expl3 and its clist or seq features?
– user31729
Dec 3, 2015 at 19:43
• @ChristianHupfer I'm not very familiar with etoolbox. If you have a simpler solution with these, just put that as an answer. Dec 3, 2015 at 19:44
• I'll try, but I am not sure about your purposes. You want a mapper, basically?
– user31729
Dec 3, 2015 at 19:52
• I have several list-like structures and would like to have one command for each of these structures by which I can easily define how these structures are displayed (e.g., a sequence of substitutions should be displayed differently than a sequence of states, but each sequence of substitutions should be displayed in the same format). Since the list-like structure is what these structures have in common, I would like to define one generic command for all of them rather than defining several commands in more or less the same way. Dec 3, 2015 at 20:01
• I've added a working version with a global \@stoplist command at the end of my solution
– user31729
Dec 4, 2015 at 12:38

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: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
#2% Checked --> is empty
}{%
% Display the various styles
{%
{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}


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}

• Thanks! Could you explain your solution a little bit? I'm not very familiar with xparse etc. and would like to get an idea how and why this works as it does (and maybe then also get an idea why my solution does not). Dec 3, 2015 at 20:37
• @cryingshadow: I wished I could explain why your versions do not work, at the moment I can't. I'll add some explanations about the code into the code
– user31729
Dec 3, 2015 at 20:40
• @cryingshadow: I'll use xparse and expl3 very frequently, although I am no expert of course -- but I can recommend it, xparse itself is easy to learn
– user31729
Dec 3, 2015 at 20:48
• @cryingshadow: You constantly call \csname @stop#1\endcsname This is never defined (so the result is \relax.)
– user31729
Dec 3, 2015 at 21:17
• True, but it should never be executed - it should be consumed by the @end#1 command. Dec 3, 2015 at 21:19

You can try this macro:

\newcount\tmpnum
\def\definelistcommand#1#2#3#4#5#6#7{%
\expandafter\def\csname\string#1:list\endcsname##1{%
\ifcase##1 #2\or#3\or#4\or#5\or#6\or#7\fi}%
\def#1{\dolist#1}%
}
\def\dolist#1#2{\tmpnum=0 \def\tmp{}%
\def\listcommand{\csname\string#1:list\endcsname}%
\dolistA#2\end}
\def\dolistA#1{%
\ifx\end#1%
\ifnum\tmpnum=0 \listcommand0%
\else\ifnum\tmpnum=1 \listcommand1\tmp\listcommand2%
\else \listcommand3\tmp\listcommand4%
\fi\fi
\else\edef\tmp{\ifx\tmp\empty\else\tmp\listcommand5\fi#1}%
\expandafter\dolistA\fi
}

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

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

\mylist{{a}{b}}

\mylist{a}

\mylist{{b}}

\mylist{}


Explanation. The \definelistcommand\foo defines two control sequences. First one \\foo:list as \ifcase. It expands the appropriate declared parameter: \\foo:list0 expnads to nothing, \\foo:list1 to [ etc. Second defined control sequence is \foo. This is macro \dolist\foo.

The usage of \foo{text} expands to \dolist\foo{text}. This macro resets \tmp to empty, \tmpnum to zero and saves \listcommand as \\foo:list. Then it runs \dolistA text\end.

The \dolistA reads the items of the text repeatedly. It saves these items to the \tmp macro using \edef\tmp. If \tmp is not empty then the comma (alias \listcommand5) is added between the previous contents and the next item #1. The \tmpnum counts the number of items. When \end is reached then \tmp is printed depending on the \tmpnum value.

Note that we need not any external package and we can declare the list-like macro very simply.

• Thanks, that works. Could you explain a little bit how it works? It seems you are using a counter for the number of elements and that several further commands are defined which might have conflicting names with other user-defined commands. Dec 3, 2015 at 20:12
• @cryingshadow If there will be more votes then I'll add the explanation. I see more votes in LaTeX 3 solution (which is absurd language), but solutions based only on classical TeX is not evaluated here. And to conflicting names with other user-defined commands: what is \hbox, \vskip etc? They are conflicting names with user-defined commands too. Dec 3, 2015 at 22:19
• I can only vote once... ;) I am just interested in how it works and what to keep in mind when using it (that is what the part on conflicting names is about - it was not intended as a criticism against your solution). Dec 3, 2015 at 22:25

Rather than a long list of arguments, I think it's better to use a key-value syntax.

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn

\NewDocumentCommand{\definelistcommand}{mm}
{% #1 is the command name, #2 is the key-value setup
% clear the temporary property list
\keys_set:nn { cryingshadow/list } { #2 }
% check if left-single or right-single have been set
{
{ \prop_item:Nn \l_cryingshadow_list_temp_prop { left } }
}
{
{ \prop_item:Nn \l_cryingshadow_list_temp_prop { right } }
}
% allocate a specific property list and make it equal to the temporary one
% define the runtime macro to call the generic one with the current name as argument
\cs_new_protected:cpn { #1 } ##1 { \cryingshadow_list_print:nn { #1 } { ##1 } }
}

% allocate some variables

% syntactic sugar
{
\prop_put:Nnn \l_cryingshadow_list_temp_prop { #1 } { #2 }
}
{
\prop_item:cn { g_cryingshadow_list_#1_prop } { #2 }
}

% define the keys
{
empty .code:n = \__cryingshadow_list_put:nn { empty } { #1 },
left-single .code:n = \__cryingshadow_list_put:nn { left-single } { #1 },
right-single .code:n = \__cryingshadow_list_put:nn { right-single } { #1 },
left .code:n = \__cryingshadow_list_put:nn { left } { #1 },
right .code:n = \__cryingshadow_list_put:nn { right } { #1 },
delimiter .code:n = \__cryingshadow_list_put:nn { delimiter } { #1 },
}

{
% split the input into items at commas
\seq_set_split:Nnn \l_cryingshadow_list_input_seq { , } { #2 }
% branch between empty or single and multiple items
\int_compare:nTF { \seq_count:N \l_cryingshadow_list_input_seq = 1 }
{
% if the sequence has just one item it could be empty
\tl_if_blank:nTF { #2 }
{ \__cryingshadow_list_item:nn { #1 } { empty } }
{
\__cryingshadow_list_item:nn { #1 } { left-single }
#2
\__cryingshadow_list_item:nn { #1 } { right-single }
}
}
{
% otherwise it has multiple items
\__cryingshadow_list_item:nn { #1 } { left }
\__cryingshadow_list_item:nn { #1 } { right }
}
}

\ExplSyntaxOff

\definelistcommand{firstlist}{
empty=empty,
left-single=[,
right-single=],
left=(,
right=),
delimiter={, },
}
\definelistcommand{secondlist}{
empty=nothing,
left=\{,
right=\},
delimiter={--},
}

\begin{document}

\firstlist{A,B,C}

\firstlist{A,B}

\firstlist{a}

\firstlist{}

\secondlist{A,B,C}

\secondlist{A,B}

\secondlist{a}

\secondlist{}

\end{document}


How does this work?

The \definelistcommand first sets up a property list specific for the list command to be defined, where to store the various items (outer delimiters, inner delimiters, what to do for the empty case); then it defines the macro to call a generic function for producing the output.

The property list is supplied the values by examining the second argument. If left-single or right-single are not specified, the values for left or right are supplied.

At call time, the generic macro takes the values from the specific property list, splits the argument at commas and then branches: if the argument is blank, the value for the case is printed; if there's only one item, the left-single and right-single outer delimiters are used; otherwise the left and right ones, with the inner delimiter between items.

• I really like the idea of key-value pairs. Seems I have to dive into xparse and friends for sophisticated macros... Dec 3, 2015 at 21:31
• @cryingshadow You can also easily supplement the list of keys, for instance you can add a key for the list separator at run time; or maybe define a constant property list with default values, so you can specify only the ones that are to be changed; you can add a check for the “single” keys and, if missing, use the “nonsingle” ones. Dec 3, 2015 at 21:35
• @cryingshadow I made some changes along the lines of my previous comment. Dec 3, 2015 at 21:51
• I just noted one thing in this solution that is problematic in my setting: It seems that this command "breaks out" of math mode. So I cannot just use it within math mode as I can, e.g., with the solution by @wipet. Dec 3, 2015 at 22:02
• @cryingshadow I have no problem and, no, it doesn't do differently in text or math mode; of course, if you plan to use \langle and \rangle as outer delimiters and the macro in math mode, you shouldn't say left=$\langle$, but left=\langle (or left=\ensuremath{\langle}`). Dec 3, 2015 at 22:05