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

share|improve this question
    
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 at 10:11
    
What do I have to use instead? As I said above, I am not that experienced. –  porst17 Apr 25 at 10:13
    
There are various tools in etoolbox or expl3. –  egreg Apr 25 at 10:14

4 Answers 4

up vote 7 down vote accepted

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}
share|improve this answer
    
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 at 10:31
    
@porst17 I added it. –  egreg Apr 25 at 10:59
    
You are right. The expl3 version is much easier to understand. Thanks a lot! –  porst17 Apr 25 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 at 12:27

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.

share|improve this answer

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.

share|improve this answer
    
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 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 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 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 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 at 19:46

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.

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.