16

I need to create a macro to render lists with a variable number of arguments (1+), e.g. \mylist{1,2,3} should expand to $\tilde{1}$--$\tilde{2}$--$\tilde{3}$. I'm trying to use xparse and \SplitList, but I didn't find a way to tell when I'm processing the last token (where I don't need the separator --).

\NewDocumentCommand\mylist{>{\SplitList{,}}m}
{
  \ProcessList{#1}{\myitem}
}
\newcommand\myitem[1]{$\tilde{#1}$--}

This would expand \mylist{1} to $\tilde{1}$-- instead of the desired $\tilde{1}$.

2 Answers2

15

Recognizing the first item is easier than the last.

There are many strategies for this.

  1. Use a conditional

    \documentclass{article}
    \usepackage{xparse}
    \NewDocumentCommand\mylist{>{\SplitList{,}}m}
    {%
      \ProcessList{#1}{\myitem}%
      \firstitemtrue
    }
    
    \newif\iffirstitem
    \firstitemtrue   
    \newcommand\myitem[1]{%
      \iffirstitem
        \firstitemfalse
      \else
        --%
      \fi
      $\tilde{#1}$}
    
    \begin{document}
    \mylist{a,b,c}
    \end{document}
    
  2. Redefine the macro at first usage; doing the processing in a group will revert \myitem to the initial meaning.

    \documentclass{article}
    \usepackage{xparse}
    \NewDocumentCommand\mylist{>{\SplitList{,}}m}
    {%
      {\ProcessList{#1}{\myitem}}%
    }
    
    \newcommand\myitem[1]{$\tilde{#1}$\let\myitem\myitema}
    \newcommand\myitema[1]{--$\tilde{#1}$}
    
    \begin{document}
    \mylist{a,b,c}
    \end{document}
    
  3. Use a different approach with expl3 macros, which is more flexible at the expense of some complication.

    \documentclass{article}
    \usepackage{xparse}
    \ExplSyntaxOn
    \NewDocumentCommand\mylist{m}
     {
      \egreg_process_list:n {#1}
     }
    % define a sequence for storing the "massaged" items
    \seq_new:N \l_egreg_items_seq
    \cs_new_protected:Npn \egreg_process_list:n #1
     {
      % clear the sequence
      \seq_clear:N \l_egreg_items_seq
      % cycle through the arguments, storing "\tilde{<arg>}" in the sequence
      \clist_map_inline:nn { #1 }
       {
        \seq_put_right:Nn \l_egreg_items_seq { $\tilde{##1}$ }
       }
      % output the sequence putting "--" in between elements
      \seq_use:Nnnn \l_egreg_items_seq { -- } { -- } { -- }
     }
    \ExplSyntaxOff
    
    \begin{document}
    \mylist{a,b,c}
    \end{document}
    

The output in all three cases is

enter image description here

Note that you still need protecting end of lines with % in the body of \NewDocumentCommand unless you're in an \ExplSyntaxOn context.

egreg
  • 1,121,712
  • An alternative to the \clist_map_inline:nn construction is to store the sequence with \seq_set_split:Nnn <seq> { , } (stable, doesn't ignore blank entries) or \seq_set_from_clist:Nn <seq> (experimental, ignores empty/blank entries), then using \seq_set_map:NNn <seq> <seq> { \exp_not:n {$\tilde{##1}$} } (experimental function), then \seq_use:Nnnn as you did. It is more efficient :-), and (perhaps) clearer. – Bruno Le Floch May 22 '13 at 09:49
5

I have used this trick often (see Cunning (La)TeX tricks). The approach is to define a list separator using a "delay". That is, define it as a definition so that the first use just defines itself:

enter image description here

\documentclass{article}
\usepackage{xparse}% http://ctan.org/pkg/xparse
\NewDocumentCommand\mylist{O{--} >{\SplitList{,}}m}
{%
  \def\itemdelim{\def\itemdelim{#1}}% Define list separator with one delay
  \ProcessList{#2}{\myitem}% Process list
}
\newcommand\myitem[1]{\itemdelim$\tilde{#1}$}

\begin{document}
\mylist{a,b,c}
\end{document}

The additional optional argument to \mylist allows you to modify the list separator (or item delimiter) of the output. It defaults to --.

Werner
  • 603,163