16

I've looked all over and can't seem to find a succinct answer to this question. Is it possible (and if so, how) to create a command that will iterate through its `arguments' of comma-separated values and act upon them?

Example mostly stolen from "For loop" in newcommand:

\newcommand{\pdfappendix}[1]{
    for \image in #1
    {
       \includegraphics[scale=0.6]{\image.pdf}
    }
}

The goal here is to create a command that can print a file menu structure (or some arbitrary path) like so:

\ppath{Command,TeXing Options,Generate PDF (C-c C-t C-p)}

potentially with an optional argument for a delimiter (defaulting to \to or something)

which would produce

Command -> TeXing Options -> Generate PDF (C-c C-t C-p)

In the words of holy ed, ?

Sean Allred
  • 27,421

4 Answers4

23

etoolbox's list processing capabilities are straight forward:

enter image description here

\documentclass{article}
\usepackage{etoolbox}% http://ctan.org/pkg/etoolbox
\newcommand{\ppath}[2][$\;\triangleright\;$]{%
  \def\nextitem{\def\nextitem{#1}}% Separator
  \renewcommand*{\do}[1]{\nextitem\textsf{##1}}% How to process each item
  \docsvlist{#2}% Process list
}
\begin{document}
A decent file path is \ppath{File,New,Document}.
\end{document}

The separator \nextitem is defined to do nothing during its first use. \do defines how each item is processed, while \docsvlist processes a comma-separated list. See Cunning (La)TeX tricks for a short discussion on the use of \nextitem.

Werner
  • 603,163
10

Found an answer in another question's answer. With @Werner's help (specifically the deferred \def trick), the pure TeX solution works without the need for extra packages. What follows is a minimal working example of what I was looking for.

\documentclass{article}

\makeatletter \newcommand{\ppath}[2][ $\triangleright$ ]{% \def\nextitem{\def\nextitem{#1}}% @for \el:=#2\do{\nextitem\el}% } \makeatother

\begin{document} A decent file path is \ppath{File,New,Why}. I said, Why. \end{document}

Output:

Output

(Thanks @Peter, @jon.)


Explanation for the comments:

\def\nextitem{\def\nextitem{#1}} here is defining \nextitem to, when it is used, redefine \nextitem to expand to the argument it was given the first time. This is a pretty tricky operation, so read that again.

So, if I call \nextitem{foo}, that expands to \def\nextitem{foo}. The \nextitem token, having been consumed and expanded, is gone. Note that \nextitem goes from having one argument to having no arguments; after using \nextitem{foo}, that itself redefines \nextitem to foo -- and doesn't emit foo the first time (since the first time is just the redefinition.

This is heart of the trick: the for loop provides a way to output >File>New>Why -- the \nextitem trickery is a way to make the first > 'disappear' -- because the first time \nextitem is used, all it's doing is a redefinition.

Sean Allred
  • 27,421
  • 1
    You need to use \@for \el:=#2\do{\nextitem\el}%. But this really should have been a separate question. – Peter Grill Dec 18 '12 at 22:26
  • 1
    It looks like you forgot a % at the end of line 6: \@for ... \el}%. – jon Dec 18 '12 at 22:27
  • I should have made it far more clear it was rhetorical (but I was actually about to ask a separate question, funnily enough). That clears up on side of it, but not the other. I found that (in the original) the macro was padding the output with a single space. It makes sense that your fix takes care of the one on the right and not on the left. I'm wondering now if the \def is giving us an intermittent space for some reason. – Sean Allred Dec 18 '12 at 22:30
  • Found it, and I'm dumb. Missed a % after the first line in my typeup. Edited. – Sean Allred Dec 18 '12 at 22:31
  • I really like your solution @SeanAllred, but I'm baffled, I can't understand it. Could you explain what's happening here? What does \def\nextitem{\def\nextitem{#1}} do to \nextitem? And if el:=#2, then \el is simply the triangle and the for loop does one iteration? – Steven Feb 08 '22 at 10:33
  • @StevenHorstink It's been a long time since I exercised my TeX-fu, but I'll try to help. My explanation got kind of long, so I included it as an edit -- hopefully that helps! – Sean Allred Feb 08 '22 at 16:15
  • 1
    @SeanAllred thanks for the quick reply. Tricky indeed, very clever. Your explanation made me realize now why I didn't understand: I mixed up the parameters, thinking #2 was the triangle. Now it all makes sense! Either way, clever solution, nice and simple. – Steven Feb 09 '22 at 10:40
4

Here is a solution with LaTeX3, which might be simpler

Since may 2021

\clist_use:nn and \clist_use:nnnn are available as pointed out by egreg in the comments.

\documentclass{standalone}
\ExplSyntaxOn
\NewDocumentCommand \ppath { O{$\;\triangleright\;$} m } {
  \clist_use:nn { #2 } { #1 }
}
\ExplSyntaxOff
\begin{document}
A decent file path is \ppath{File,New,Document}.
\end{document}

The output is

enter image description here

Before may 2021

The instruction \clist_use:nn { #2 } { #1 } should be replaced by

  \group_begin:
  \clist_set:Nn \l_tmpa_clist { #2 }
  \clist_use:Nn \l_tmpa_clist { #1 }
  \group_end:

Some explanations:

In the second example,

  1. We wrap the commands in a group to use local variables
  2. We store the mandatory argument in a scratch variable named \l_tmpa_clist of type clist
  3. We "use" that variable to display the list with the given separator

Notice the use of \clist_use:Nn instead of \clist_use:nn.

The LaTeX3 commands documentation is available on CTAN as interface3.pdf and may be available locally with texdoc interface3.

  • +1 This has also the advantage that spaces around commas will be ignored. – egreg Dec 29 '20 at 10:47
  • +1 Another advantage is that blankness/emptiness between commas will not lead to mapping to an empty element. I.e., with \ppath{File, ,,New,Document,} neither you get three triangles behind "File" but you get only one triangle, nor you get a triangle behind "Document". – Ulrich Diez Dec 29 '20 at 15:44
  • To be even more complete, one can use a void group {} to have a blank item that is not discarded: with \ppath{File, {} ,{},New,Document,{}} you obtain as many triangles as there are commas. – Jérôme LAURENS Dec 29 '20 at 16:05
  • Since May 2021, it's no longer needed to set a variable, so the group is unnecessary and \clist_use:nn { #2 } { #1 } is even fully expandable. – egreg Feb 08 '22 at 16:23
1

In OpTeX we have \foreach command with the syntax

\foreach <list of object>\do <parameter-mask for sigle object>{<what to do>}

For example

\foreach a,b,c,d,\do #1,{do something with #1}

does (expadable) loop over the given list of objects separated by comma. Your task should be solved by the following macro:

\def\ppath#1{\ppathA#1,\end}
\def\ppathA#1,#2\end{#1\foreach #2\do ##1,{$\,\triangleright\,$##1}}

\ppath{Command,TeXing Options,Generate PDF (C-c C-t C-p)}

\ppath{Cmd}

\bye

wipet
  • 74,238