4

I posted these two question

  1. Tricky macro expansion
  2. Tricky list

Now I would like to merge the two issues:

\documentclass{article}

\makeatletter

\newcounter{a@c}
\newcounter{b@c}
\newcounter{c@c}
\newcounter{dummycounter}
\newtoks\a@t
\newtoks\c@t
%\newcommand{\comma}{[[\thea@c,\theb@c]]\stepcounter{b@c}}

\newcommand{\Aa}[1]{%
 \ifnum\thea@c>0\a@t=\expandafter{\the\a@t\@elt\relax #1}%
 \else\a@t=\expandafter{#1}%
 \fi%
 \stepcounter{a@c}
}

\newcommand{\printauthor}{%
  \setcounter{dummycounter}{0}%
  \def\@elt##1{%
    \ifnum\c@dummycounter < \numexpr\c@a@c - 2\relax%
    , 
    \else
    { and }%
    \fi
    \stepcounter{dummycounter}%
  }
}


\newcommand{\reset}{%
  \setcounter{a@c}{0}
  \a@t={}
  %\newline
}

\newcommand{\Af}{%
  \printauthor
  \begingroup
   \ifnum\thec@c>0
    \edef\x{\endgroup\noexpand\c@t={\the\c@t, (\the\a@t)}}%
   \else
    \edef\x{\endgroup\noexpand\c@t={(\the\a@t)}}%
   \fi
   \x
   \stepcounter{c@c}%
   \reset%
   %\newline
}

\newcommand{\Pp}{\the\c@t}

\makeatother


\begin{document}

\Aa{a}
\Aa{b}
\Aa{c}
\Af
\Aa{d}
\Af
\Aa{pippo}
\Aa{pluto}
\Af
\Aa{paperino}
\Af
\Pp
\end{document}

The result I get is

(a and b and c), (d), (pippo and pluto), (paperino)

but is unwanted since I would like to obtain

(a, b and c), (d), (pippo and pluto), (paperino)

How can I fix this. The fact is that it seems very simple, in python or C or any other language would be easy. It seems to me that in TeX even the most simple thing requires tons of lines of code and tricks... Can you help me?

  • 1
    I think I'd be more impressed with the final comment about Python and C is if you added that Python and C can also typeset complex documents using only a few lines of Python or C code. (Or is it that Python and C were designed to do very different things than TeX?) – jon May 5 '16 at 2:55
  • 1
    In C you would have to set up dynamic memory allocation and probably a linked list data structure just to make a function where you could keep adding to a list without specifying the length of the list. And that's just for output to the command line, not positioning characters from a font file on a page in the language of a device driver. – musarithmia May 5 '16 at 20:47
3

The important thing in the application you seem to have in mind is not forcing a too rigid setting.

Thus I store each authors' list as itself, but as the argument to another command, which can be set at usage time in a variety of ways.

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn

\NewDocumentCommand{\addauthors}{m}
 {
  \mapo_addauthors:n { #1 }
 }

\NewDocumentCommand{\printauthors}{+O{,~}}
 {
  \mapo_printauthors:n { #1 }
 }

\seq_new:N \g_mapo_authors_seq
\seq_new:N \l__mapo_temp_seq

\cs_new_protected:Nn \mapo_addauthors:n
 {
  \seq_gput_right:Nn \g_mapo_authors_seq { \__mapo_wrapper:n { #1 } }
 }

\cs_new_protected:Nn \mapo_printauthors:n
 {
  \cs_set_eq:NN \__mapo_wrapper:n \__mapo_standardprint:n
  \seq_use:Nn \g_mapo_authors_seq { #1 }
 }
\cs_new_protected:Nn \__mapo_standardprint:n
 {
  \seq_set_split:Nnn \l__mapo_temp_seq { \\ } { #1 }
  \seq_use:Nnnn \l__mapo_temp_seq { ~and~ } { ,~ } { ~and~ }
 }
\ExplSyntaxOff

\begin{document}

\addauthors{
  a \\
  b \\
  c
}

\addauthors{d}

\addauthors{pippo \\ pluto}

\addauthors{paperino}

\printauthors

\medskip

\printauthors[\par\medskip]

\end{document}

As you see, the code is not much longer than the others presented and is very flexible.

enter image description here

  • Thank you for the solution! Where can I find some reference to use xparse?The official manual seems to be poor. For instance what is the meaning of \clist or \l? – MaPo May 5 '16 at 12:53
  • @MaPo texdoc interface3 – egreg May 5 '16 at 12:53
  • Finally, I'm having one funny problem: it seems that inside \clist_put:Nx it's not allowed to put \textit{...} – MaPo May 5 '16 at 14:50
  • @MaPo My solution doesn't use \clist_put:Nx, you should ask cfr about it. – egreg May 5 '16 at 14:51
6

If I see given "user environment" (last twelve lines in your code) and desired output, then I can say: it is very simple to do it at TeX primitive level:

\def\alist{}\def\plist{}
\def\Aa#1{\edef\alist{\alist,\detokenize{#1}}}
\def\Af{\edef\plist{\ifx\plist\empty\else\plist, \fi
       (\ifx\alist\empty\else\expandafter\applyand\alist,,\fi)}%
   \def\alist{}%
}
\def\Pp{\scantokens\expandafter{\plist}}
\def\applyand #1,#2,#3,{%
   \ifx,#1,\else \ifx,#3, and \else, \fi\fi #2%
   \ifx,#3,\else \fihere \applyand #2,#3,\fi
}
\def\fihere#1\fi{\fi#1}

\Aa{a}
\Aa{b}
\Aa{c}
\Af
\Aa{d}
\Af
\Aa{pippo}
\Aa{pluto}
\Af
\Aa{paperino}
\Af
\Pp
  • Your solution seems to be very elegant. The problem is that it seems hard to format text. For instance I would like to print the final list in character with different style and size. Is this possible? – MaPo May 5 '16 at 12:23
  • @MaPo Where is a problem? These macros do only what you want. The formating style is another story independent of this. My macros work in all formats because they depend only on TeX. – wipet May 5 '16 at 12:29
  • For instance how to apply \Large or \MakeUppercase to the expression in \Aa? – MaPo May 5 '16 at 12:31
  • @MaPo OK, I'v added using eTeX primitives \detokenize and \scantokens in order you can write \Aa{{\bf p}ippo}, for example. – wipet May 5 '16 at 19:26
  • 2
    @Manuel No, \alist is totally \edefed in context \expandafter\applyand\alist. Of course, I am able to dispense eTeX primitives (if needed) here but I want to be my code most short and straightforward (as usual:). – wipet May 5 '16 at 19:52
4

I now see from your other questions that, despite your comments concerning how easy it is to do this in modern programming languages, you are determined to compare those languages with TeX, rejecting the modern interfaces which build upon it. Little wonder, then, that the TeX case seems more difficult. For a fair comparison, you need to compare other languages available at the time TeX was created or, at least, finalised. Perhaps other languages available in the early 80s did make this easy - I wouldn't know. But comparing it with today's Python and C is comparing apples and oranges i.e. by no means fair!

Here's an implementation using a modern interface which requires no tricks and relatively few lines of code. Admittedly, the names of macros etc. are longer than those used by TeX but the pay-off is code which is much more transparent and readable.

Nor do I recommend macros like \Aa as an end user interface. They make your question significantly harder to understand and will make the commands significantly harder to use. This is true even if you are the only end user. Perhaps even especially in that case, since, if you are like me, you don't write documentation for code intended only for private use.

In any case, we can use fewer lines by far of code because we do not need to reinvent perfectly good wheels.

Here, \addauthor{} adds an author; \authorout finishes an author group; and \printout prints out the entire list. Since I've basically no idea what you're trying to do, the facilities provided are likely not ideal for your purposes, but they should give you the basic idea.

Two comma separated lists are defined. One holds the current author group. The other holds the overall list of author groups. We don't need to count anything because when we use the comma list, we can tell TeX which separators should be used between items in a 2-item list, the final 2 items in a 3+-item list and the remaining pairs of items in a 3+-item list. We also have the facilities required to clear the lists as necessary. Here, I assume that the complete list should be cleared after typesetting.

Note that full expansion is activated simply by using the appropriate signature when calling the macro to use the comma list holding the current author group.

  \clist_put_right:Nx \l_mapo_authors_clist { ( \clist_use:Nnnn \l_mapo_author_clist { ~and~ } { ,~ }  { ~and~ } ) }

is used to add the contents of the completed author group to the main list of author groups. The :Nx indicates that the second argument should be fully expanded, ensuring that we get the content of the list added to the main list before clearing it in the next line in readiness for the next author group.

In contrast

  \clist_put_right:Nn \l_mapo_author_clist { #1 }

has the signature :Nn which indicates the second argument should not be treated specially, as that is not required here.

Hence, there is no need to worry about how many \expandafters to use or where to put them.

\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\clist_new:N \l_mapo_author_clist
\clist_new:N \l_mapo_authors_clist
\tl_new:N \l_mapo_end_tl
\tl_set:Nn \l_mapo_end_tl {}
\NewDocumentCommand \addauthor { m }{
  \clist_put_right:Nn \l_mapo_author_clist { #1 }
}
\NewDocumentCommand \authorout {}{
  \clist_put_right:Nx \l_mapo_authors_clist { ( \clist_use:Nnnn \l_mapo_author_clist { ~and~ } { ,~ }  { ~and~ } ) }
  \clist_clear:N \l_mapo_author_clist
}
\NewDocumentCommand \printout {}{
  \clist_use:Nnnn \l_mapo_authors_clist { ~, } { ,~ } { ,~ }
  \l_mapo_end_tl
  \clist_clear:N \l_mapo_authors_clist
}
\ExplSyntaxOff
\begin{document}
\addauthor{a}
\addauthor{b}
\addauthor{c}
\authorout
\addauthor{d}
\authorout
\addauthor{pippo}
\addauthor{pluto}
\authorout
\addauthor{paperino}
\authorout
\printout
\end{document}

entire list

EDIT

To answer the question in comments, the aim is to modify the above code to produce the following output:

entire list: modified format

To do this, I would modify the addition of each author rather than the addition of each author group to the overall list. To do this, we can simply test whether the comma list holding the current author group is empty when we add an author. If it is empty, then we prefix the addition to the list with the required label. If not, then we simply add the new addition as usual.

\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\clist_new:N \l_mapo_author_clist
\clist_new:N \l_mapo_authors_clist
\tl_new:N \l_mapo_end_tl
\tl_set:Nn \l_mapo_end_tl {}
\NewDocumentCommand \addauthor { m }{
  \clist_if_empty:NTF \l_mapo_author_clist
  {
    \clist_put_right:Nn \l_mapo_author_clist { \textsc{Authors:~} \textit{#1} }
  }
  {
    \clist_put_right:Nn \l_mapo_author_clist { \textit{#1} }
  }
}
\NewDocumentCommand \authorout {}{
  \clist_put_right:Nx \l_mapo_authors_clist { \clist_use:Nnnn \l_mapo_author_clist { ~and~ } { ,~ }  { ~and~ } }
  \clist_clear:N \l_mapo_author_clist
}
\NewDocumentCommand \printout {}{
  \clist_use:Nnnn \l_mapo_authors_clist { ~, } { ,~ } { ,~ }
  \l_mapo_end_tl
  \clist_clear:N \l_mapo_authors_clist
}
\ExplSyntaxOff
\begin{document}
\addauthor{a}
\addauthor{b}
\addauthor{c}
\authorout
\addauthor{d}
\authorout
\addauthor{pippo}
\addauthor{pluto}
\authorout
\addauthor{paperino}
\authorout
\printout
\end{document}

Note that to get the space between Authors: and the name of the author, we need ~ rather than because is ignored whenever expl3 syntax is active. (This is why it is not necessary to avoid spaces we don't want or to comment line endings to avoid spurious spaces as we would normally have to do. The downside is that all spaces we do want must be explicitly coded. In this context ~ is a breakable space - not an unbreakable one - as can be seen from the output of the final author group above.

  • Thank you for the solution! Where can I find some reference to use xparse?The official manual seems to be poor. For instance what is the meaning of \clist or \l? – MaPo May 5 '16 at 12:52
  • Finally, I'm having one funny problem: it seems that inside \clist_put:Nx it's not allowed to put \textit{...} – MaPo May 5 '16 at 14:54
  • @MaPo Look at interface3.pdf which is part of the l3kernel documentation. – cfr May 5 '16 at 15:05
  • What do you want in italics exactly? For example \clist_put_right:Nn \l_mapo_author_clist { \textit{#1} } works easily. Do you want everything in italics or only some parts? – cfr May 5 '16 at 15:09
  • \clist_put_right:Nx \l_mapo_authors_clist { \textsc{Authors: } \clist_use:Nnnn \l_mapo_author_clist { ~and~ } { ,~ } { ~and~ } )} \clist_clear:N \l_mapo_author_clist Doesn't work. If I substitute Nx with Nn I see only \textsc{Authors} – MaPo May 5 '16 at 15:13
2

Because some times we like to reinvent the wheel. Here is a an approach that allows you to alter the format on the fly among other things.

\documentclass{article}
\usepackage{etoolbox}
\usepackage[T1]{fontenc}
\usepackage{lmodern}

\makeatletter
%% ----------------------- %%                                          
%% --- Defining a list --- %%                                          
%% ----------------------- %%                                          
%% Define a new list control sequence.                                 
%% No special bells or whistles here.  Be careful!!                    
\newcommand\aeNewList[1]{\def#1{}}


%% ----------------------- %%                                            
%% --- Creating a list --- %%                                            
%% ----------------------- %%                                            
%% Once you've created a list, you can add elements to it.               
%% I allow you to add as many elements at a time.  Need to               
%% test whether you're just adding one or more tokens to be              
%% sure not to prematurely split the first element when it's             
%% the only thing being added to the list.                               
%%                                                                       
%% Warning!!!  This will not quite break if you try to create a list with
%% with exactly one element in it.                                       
\newcommand\aeAddToList{\@ae@create@list@fork}                         

\def\@ae@create@list@fork#1#2#3;{%%
  \expandafter\ifx\expandafter\relax\detokenize{#3}\relax
    \ae@add@singleton@to@list#1#2;%%
  \else
    \ae@add@many@to@list#1#2#3\relax;%%
  \fi
  }

\def\ae@add@singleton@to@list#1#2;{%%
  \def\ae@b{#2}%%
  \edef#1{\expandonce#1{\expandonce\ae@b}}}

\def\ae@add@many@to@list#1#2#3;{%%
  \def\ae@continue{}%%
  \def\ae@b{#2}%%
  \def\ae@test@relax{#3}%%
  \edef#1{\expandonce#1{\expandonce\ae@b}}%%
  \if\ae@test@relax\relax
  \else
    \def\ae@continue{\ae@add@many@to@list#1#3;}%%
  \fi
  \ae@continue
}

%% ----------------------- %%
%% --- Printing a list --- %%
%% ----------------------- %%
%% Again a forking command because you may want to treat
%% a binary list differently from a longer list.
\newcommand\printMyList[1]{\expandafter\@ae@print@list@fork#1\relax;}
\def\@ae@print@list@fork#1#2#3;{%%
  \ae@list@open@symbol
  \def\ae@test@relax{#3}%%
  \def\ae@continue{}%%
  \if\ae@test@relax\relax
    \def\ae@continue{\ae@print@binary@list{#1}{#2}}%%
  \else
    \def\ae@continue{\ae@print@long@list{#1}#2#3;}%%
  \fi
  \ae@continue
}

\def\ae@print@binary@list#1#2{%%
    #1%%
    %% lists really should have more than one element in them!!!!
    \ifx\relax#2%%
    \else
      \ae@list@short@join@string#2%%
    \fi
  \ae@list@shut@symbol}

\def\ae@print@long@list#1#2#3;{%%
  \def\ae@test@relax{#3}%%
  \def\ae@continue{\ae@list@shut@symbol}%%
  \if\ae@test@relax\relax
    #1\ae@list@long@final@join@string#2%%
  \else
    #1\ae@list@long@interim@join@string
    \def\ae@continue{\ae@print@long@list#2#3;}%%
  \fi
  \ae@continue}

%% ------------------------- %%
%% --- Formatting a list --- %%
%% ------------------------- %%
%% An interface to control the presentation of the final list.
%% I suppose that this could have been made to be tied to a 
%% particular list control sequence, but I don't have that much 
%% time to kill right now.  So, I leave that to someone else.
\newcommand\aeFormatList[5]{%%
  \def\ae@list@open@symbol{#1}%%
  \def\ae@list@short@join@string{#2}%%
  \def\ae@list@long@interim@join@string{#3}%%
  \def\ae@list@long@final@join@string{#4}%%
  \def\ae@list@shut@symbol{#5}}%%

%% ----------------------- %%
%% --- Clearing a list --- %%
%% ----------------------- %%
%% Clear out the list control sequence to use for a new list.
\newcommand\aeClearList[1]{\def#1{}}

\makeatother

\begin{document}
\aeNewList\myList
\aeFormatList{(}{ and }{, }{, and }{)}%%


\aeAddToList\myList{\emph{A}};%%
\aeAddToList\myList{B};%%
\aeAddToList\myList{C}{D}{E};%%
A long list of elements: \printMyList\myList.


\aeClearList\myList%%
\aeAddToList\myList{A};%%
\aeAddToList\myList{B};%%
A list with two elements: \printMyList\myList


\aeClearList\myList%%
\aeAddToList\myList{A};%%
Not such a good list: \printMyList\myList


\aeFormatList{}{ OR }{$\rightarrow$}{$\vee$}{!}%%
\aeClearList\myList%%
\aeAddToList\myList{M};%%
\aeAddToList\myList{N};%%
A list with two elements: \printMyList\myList

\aeAddToList\myList{O}{P};%%
A long list of elements: \printMyList\myList

\end{document}

enter image description here

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.