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

7
  • 1
    There are plenty of questions for lists see tex.stackexchange.com/a/19761/963 and maybe you can post a more specific question
    – yannisl
    Commented Dec 17, 2012 at 21:03
  • 1
    Can you explain in how far the answers in the questions you linked don't fulfil your needs? Commented Dec 17, 2012 at 21:13
  • For a more specific question, see Understanding a \@for loop.
    – Alan Munn
    Commented Dec 17, 2012 at 21:22
  • It would also be beneficial to peruse the posts tagged with comma-separated-list.
    – Werner
    Commented Dec 17, 2012 at 21:44
  • Actually did that. I'll go through them again - I skipped a few where I didn't foresee the general answer. Commented Dec 17, 2012 at 22:29

4 Answers 4

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.

5
  • 2
    Very clever fix for the first item bit. Commented Dec 17, 2012 at 23:35
  • As highlighted in adding \chapter in KOMA scrbook generates a “! LaTeX Error: \do undefined.”, using the \do command might lead to errors because numerous other packages/macros redefines it (e.g. notably \chapter of KOMA's classes). egreg thus suggests here a variant of this answer without using \do.
    – ebosi
    Commented May 1, 2017 at 13:41
  • Is it possible to handle the last item differently like making \ppah{a,b,c} produce "a, b and c"?
    – Mr. Ree
    Commented Aug 23, 2017 at 13:17
  • 1
    @Mr.Ree: Try this code (output).
    – Werner
    Commented Aug 23, 2017 at 20:13
  • @Werner Thanks for your nice code! I've only found some codes which are too complicated to understand before.
    – Mr. Ree
    Commented Aug 25, 2017 at 9:45
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.

7
  • 1
    You need to use \@for \el:=#2\do{\nextitem\el}%. But this really should have been a separate question. Commented Dec 18, 2012 at 22:26
  • 1
    It looks like you forgot a % at the end of line 6: \@for ... \el}%.
    – jon
    Commented Dec 18, 2012 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. Commented Dec 18, 2012 at 22:30
  • Found it, and I'm dumb. Missed a % after the first line in my typeup. Edited. Commented Dec 18, 2012 at 22:31
  • 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
    Commented Feb 9, 2022 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.

4
  • +1 This has also the advantage that spaces around commas will be ignored.
    – egreg
    Commented Dec 29, 2020 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". Commented Dec 29, 2020 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. Commented Dec 29, 2020 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
    Commented Feb 8, 2022 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
 

You must log in to answer this question.

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