8

I am trying to build a command similar to the \author command. It takes one argument with is actually a list of arguments separated by \and. The command needs to apply another command like \underline or \emph to each element of the list. My current approach is

\documentclass{minimal}
\newcommand{\mylist}[1]{%
    \def\and{\noexpand\endgroup \noexpand\underline\noexpand\begingroup}%
    \edef\myinternallist{\noexpand\underline\noexpand\begingroup #1\noexpand\endgroup}%
    \show\myinternallist%
    \myinternallist%
}
\begin{document}
    \mylist{Element1 \and Element2}
\end{document}

and \show\myinternallist yields

> \myinternallist=macro:
->\underline \begingroup Element1 \endgroup \underline \begingroup Element2\endgroup .

which looks ok to my untrained eye. However, the above code does'nt compile and I have no clue why it does'nt. The expected result looks like this: enter image description here

3
  • First of all, \underline\begingroup abc\endgroup is illegal: you can't use \begingroup and \endgroup for delimiting the argument to a macro.
    – egreg
    Apr 25, 2014 at 10:11
  • What do I have to use instead? As I said above, I am not that experienced.
    – porst17
    Apr 25, 2014 at 10:13
  • There are various tools in etoolbox or expl3.
    – egreg
    Apr 25, 2014 at 10:14

4 Answers 4

7

You indeed get

\underline \begingroup Element1 \endgroup \underline \begingroup Element2\endgroup

but unfortunately \underline\begingroup Element1 \endgroup is illegal, because \begingroup and \endgroup can't be used for delimiting a macro argument.

There are various tools for this; I'll show a solution based on expl3, the programming layer of the future LaTeX3.

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\mylist}{ O{\underline} m }
 {
  % transfer control to an internal function
  \porst_mylist:nn { #1 } { #2 }
 }

\seq_new:N \l__porst_list_items_seq
\seq_new:N \l__porst_list_output_seq

\cs_new_protected:Npn \porst_mylist:nn #1 #2
 {
  % clear the output sequence
  \seq_clear:N \l__porst_list_output_seq
  % split the input at \and
  \seq_set_split:Nnn \l__porst_list_items_seq { \and } { #2 }
  % append each item to the output sequence
  \seq_map_inline:Nn \l__porst_list_items_seq
   {
    % #1 is the given argument, ##1 represents the current item
    \seq_put_right:Nn \l__porst_list_output_seq { #1 { ##1 } }
   }
  % output the sequence with something between items
  \seq_use:Nn \l__porst_list_output_seq {,~} % adjust
 }
\ExplSyntaxOff

\begin{document}

\mylist{Element1 \and Element2}

\mylist[\emph]{Element1 \and Element2}

\end{document}

The first step is splitting the input at the \and tokens; trailing and leading spaces will be removed; the second step is storing the “formatted” items in another sequence, for using this one with some separator between the items.

enter image description here

Another possible tool is etoolbox; but, as you see, it's much more complicated.

\documentclass{article}
\usepackage{etoolbox}

\makeatletter
\DeclareListParser*{\andlist@do}{\and}
\newcommand{\andlist@sep}{, }
\newcommand{\mylist}[2][\underline]{%
  \def\andlist@output{\@gobble}%
  \andlist@do{\andlist@handler{#1}}{#2}%
  \andlist@output
}
\appto\nocorrlist{\andlist@sep}

\newcommand{\andlist@handler}[2]{%
  \appto\andlist@output{\andlist@sep#1{#2\unskip}}%
}
\makeatother

\begin{document}

\mylist{Element1 \and Element2}

\mylist[\emph]{Element1\and Element2}

\end{document}
4
  • Would you mind providing a solution with etoolbox as well? I already use the etoolbox package and I would like to avoid loading an additional package like xparse.
    – porst17
    Apr 25, 2014 at 10:31
  • @porst17 I added it.
    – egreg
    Apr 25, 2014 at 10:59
  • You are right. The expl3 version is much easier to understand. Thanks a lot!
    – porst17
    Apr 25, 2014 at 12:24
  • @porst17 With etoolbox it's more difficult to remove unwanted spaces and also to insert something only between items.
    – egreg
    Apr 25, 2014 at 12:27
8

If you want a solution not requiring a package, use braces {}, but you'll need balance them in the definition. To hidden them when expanded just use \iffalse .. \fi:

\def\and{\unskip\iffalse{\fi} \noexpand\underline{\iffalse}\fi}%
\protected@edef\myinternallist{\noexpand\underline{#1}}%

\edefcannot be used with arbitrary stuff, so I use \protected@def, which means you must enclose the whole definition with \makatletter .. \makeatother. I've also added \unskip (to remove the space at the end of each block) and a space.

4

Here is a common solution without any expl3, etoolbox etc. The \and macro can be defined as required. We need only three lines of macros:

\def\mylist#1{\mylistA #1\and\and}
\def\mylistA#1\and{\ifx\and#1\and\else \and{#1}\expandafter\mylistA\fi}
\def\and#1{$\underline{\rm #1}$ }

\mylist{Element1 \and Element2}

Edit: Egreg's comment here includes question, how to print separator only between items. This is simple. We stay in three lines and without additional packages and without any hassle:

\def\mylist#1{\def\mylistS{\def\mylistS{, }}\mylistA #1\and\and}
\def\mylistA#1\and{\ifx\and#1\and\else \and{#1}\expandafter\mylistA\fi}
\def\and#1{\mylistS $\underline{\rm #1}$}

\mylist{Element1 \and Element2}

Edit2: If somebody needs to print another separator between last two items then he can use following code. Of course, it is slight more complicated:

\def\mylist#1{\def\mylistS{}\def\andA##1{\let\andA=\andB}\mylistA #1\and\and}
\def\mylistA#1\and{\ifx\and#1\and \and{}\else \and{#1}\expandafter\mylistA\fi}
\def\and#1{\ifx\mylistS\empty\else\andA{#1}\fi\mylistS \def\mylistS{$\underline{\rm #1}$}}
\def\andB#1{\ifx\and#1\and{ and }\else{, }\fi}

The comma is printed between elements but the word " and " is printed between last two elements.

6
  • There is a trailing space, which might be undesirable in some contexts; what if one wants a “comma and space” only between the items and no trailing comma? With \seq_use:Nn you get it very easily, without any hassle; with \seq_use:Nnnn you can also set a separator for two items only and for more than two items, with special treatment for the last two.
    – egreg
    Oct 12, 2014 at 19:35
  • 2
    I don't see any hassle when I am using TeX primitives. IMHO a big hassle is usage of expl3.
    – wipet
    Oct 13, 2014 at 9:02
  • It's not a challenge like “do something using as few tokens as possible”. I know it's possible using primitives; but I also know that I don't want to reinvent the wheel each time I need a basic construct. High level programming languages were born just for this. If you want to program everything in Assembler, you're welcome, but don't dare say that it's simpler than with C.
    – egreg
    Oct 13, 2014 at 18:07
  • Writting four lines of code isn't reinventing the wheel. It is normal praxis of TeX progamming. On the other hand I don't need to know thousands macros and its parameters for various usage. The knowledge of 300 primitives is sufficient.
    – wipet
    Oct 13, 2014 at 18:17
  • 2
    The "Assembler to C" is bad comparison. Assemler (and machine code) is designed to implement new level (ie. C, for example). But TeX isn't designed for this. The bad idea is to forget that there exists TeX. If people know TeX then there would be much less problems at tex.sx.com. I only remind that there is TeX and that the problems here can be solved by TeX only by few lines of code.
    – wipet
    Oct 13, 2014 at 19:46
1

If you are not bound by the idea to limit items by \and you may want to take a look at the packages commado or dowith that I recently discovered when having a similar problem.

With the former you could try something along the lines

    \usepackage{commado}
    % ...
    \newcommand{\myList}[2][\underline]{\doWithCSL{#1}{#2}}

and call it like

    \myList{Element1, Element2}

As this would not produce any space between the elements, the following additions will (almost) reproduce the above examples

    \usepackage{commado}
    \usepackage{xspace}
    \newcommand{\myul}[1]{\underline{#1}\xspace}
    \newcommand{\myem}[1]{\emph{#1}\xspace}
    \newcommand{\myList}[2][\myul]{\doWithCSL{#1}{#2}}
    % ...
    \mylist{Element1, Element2}!

    \mylist[\myem]{Element1, Element2}!

(the exclamation mark shows that the \xspace helps suppressing the trailing space if not needed)

All packages are part of the standard distribution.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .