3

I know that the package etoolbox has a \foreach command.

I want to set a list once an use it multiple times. In the example, I would set \mylist equal to the comma separated value list 10,20,30,40,50.

I want to have a command like

\forparteach 3 \var \in {10,20,30, 40,50} {
  \var
}

is the same as

\foreach \var \in {10,20,30} {
   \var
}

Or even

\forparteach {1,3,4,2,5,5} \var \in {10,20,30,40,50} {
   \var
}

is the same as

\foreach \var \in {10,30,40,20,50,50} {
   \var
}

Is there any package out there that does this or an easy solution?

Thanks!

  • How would you distinguish between \forparteach 3 picking every third element and not just the (single) 3rd element? – Werner Sep 9 '18 at 17:04
0

Both of the things can be achieved with the current \foreach in a straightforward way. More specifically, \breakforeach allows you to break a loop, which allows you to restrict the loop to the first n entries. And you can store the list in an array and access its elements. Notice that I did not even try to write a new macro \forparteach because \foreach is very delicate and I do not dare to mess around with it.

\documentclass{article}
\usepackage{pgffor}
\begin{document}
\section*{Cutting a loop off at some number}
\foreach \var [count=\n] in {10,20,30,40,50} 
{ \var 
\ifnum\n>2 
\breakforeach 
\fi
}
\section*{Selecting a subset of items from a list/an array}
\def\myarray{{10,20,30,40,50}}
\foreach \var [evaluate=\var as \myindex using {int(\var-1)}] in {1,3,4,2,5,5} {
\pgfmathparse{\myarray[\myindex]}\pgfmathresult
}
\end{document}

enter image description here

| improve this answer | |
1

You can implement your own foreach cycle.

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\xforeach}{sO{}mm}
 {
  \IfBooleanTF { #1 }
   {
    \marks_xforeach:Vnn #3 { #2 } { #4 }
   }
   {
    \marks_xforeach:nnn { #3 } { #2 } { #4 }
   }
 }

\keys_define:nn { marks/xforeach }
 {
  upto  .int_set:N   = \l_marks_xforeach_upto_int,
  items .clist_set:N = \l_marks_xforeach_items_clist,
 }

\clist_new:N \l__marks_xforeach_main_clist
\seq_new:N \l__marks_xforeach_items_seq
\seq_new:N \l__marks_xforeach_main_seq

\cs_new_protected:Nn \marks_xforeach:nnn
 {
  \clist_set:Nn \l__marks_xforeach_main_clist { #1 }
  \keys_set:nn { marks/xforeach } { upto=-1, items=, #2 }
  \cs_set_protected:Nn \__marks_xforeach_cycle:n { #3 }

  \seq_clear:N \l__marks_xforeach_items_seq
  \clist_if_empty:NTF \l_marks_xforeach_items_clist
   {% items not specified
    \int_compare:nT { \l_marks_xforeach_upto_int = -1 }
     {
      \int_set:Nn \l_marks_xforeach_upto_int
       { \clist_count:N \l__marks_xforeach_main_clist }
     }
    \int_step_inline:nn { \l_marks_xforeach_upto_int }
     {
      \seq_put_right:Nn \l__marks_xforeach_items_seq { ##1 }
     }
   }
   {% items
    \seq_set_from_clist:NN \l__marks_xforeach_items_seq \l_marks_xforeach_items_clist
   }
  \seq_clear:N \l__marks_xforeach_main_seq
  \seq_map_inline:Nn \l__marks_xforeach_items_seq
   {
    \seq_put_right:Nx \l__marks_xforeach_main_seq
     {
      \clist_item:Nn \l__marks_xforeach_main_clist { ##1 }
     }
   }
  \seq_map_function:NN \l__marks_xforeach_main_seq \__marks_xforeach_cycle:n
 }
\cs_generate_variant:Nn \marks_xforeach:nnn { V }

\ExplSyntaxOff

\begin{document}

\newcommand{\myarray}{10,20,30,40,50}

\xforeach{10,20,30,40,50}{(Item: #1) }

\xforeach*{\myarray}{(Item: #1) }

\xforeach[upto=3]{10,20,30,40,50}{(Item: #1) }

\xforeach*[upto=3]{\myarray}{(Item: #1) }

\xforeach[items={1,3,4,2,5,5}]{10,20,30,40,50}{(Item: #1) }

\xforeach*[items={1,3,4,2,5,5}]{\myarray}{(Item: #1) }

\end{document}

enter image description here

Some points to note: differently from \foreach, the cycles are not performed in groups; the current item is denoted by #1 rather than by designating a macro holding it. The *-variant accepts as second argument a macro instead of an explicit list.

One could rely on the presence of braces like \foreach, but in my opinion using options and specific commands is cleaner.

| improve this answer | |
  • +1. This looks like a promising alternative to the \foreach that comes with pgffor. Do you think it is possible to define a cousin of the standard \foreach this way, which does not produce a lot of groups? I am asking this because that may help in some situations like this one. Of course, I will be happy to ask a separate question on this if you feel that's more appropriate. – user121799 Sep 9 '18 at 23:35
  • @marmot Something like in this paper? – egreg Sep 10 '18 at 8:23
  • Si, signore. Except that I am wondering if it is possible to recreate all the nice things of \foreach like \foreach \X in {1,3,...,17,18,19}, \foreach \X,\Y in {1/red,2/blue,3/green} and also the evaluate, count and remember keys. Ho mangiato pizza senza pineapple. – user121799 Sep 10 '18 at 11:18
  • 1
    @marmot I don't like the syntax of \foreach. The evaluate and so on things are in a long term program. The double variable cycles are already implemented, if you look better. – egreg Sep 10 '18 at 11:21
0

You can do these things with xinttools. The \xintFor/\xintFor* loops do not create groups. On the other hand, they do not currently implement some nice \foreach syntax like 1, 2, ..., 5. And there is no easy way to create key=value selection as xinttools is not a full-fledged programming interface like provided by LaTeX3, but a small collection of useful macros (usable under Plain TeX, too).

\documentclass{article}

\usepackage{xinttools}
\usepackage{parskip}
\begin{document}\pagestyle{empty}\thispagestyle{empty}

\section*{Cutting a loop off at some number}

First three:

\begin{itemize}
% this uses \xintFor* as argument will be {item1}{item2}....
% i.e. a "list" in the sense of xinttools doc, not a
% "comma separated list"
 \xintFor* #1 in {\xintKeep{3}{\xintCSVtoList{10,20,30,40,50}}}
 \do 
  {\item (Item: #1)}
\end{itemize}

Last two:

\begin{itemize}
 \xintFor* #1 in {\xintKeep{-2}{\xintCSVtoList{10,20,30,40,50}}}
 \do 
  {\item (Item: #1)}
\end{itemize}

Trim first two:

\begin{itemize}
 \xintFor* #1 in {\xintTrim{2}{\xintCSVtoList{10,20,30,40,50}}}
 \do 
  {\item (Item: #1)}
\end{itemize}

Trim last two:

\begin{itemize}
 \xintFor* #1 in {\xintTrim{-2}{\xintCSVtoList{10,20,30,40,50}}}
 \do 
  {\item (Item: #1)}
\end{itemize}

Nested keep/trim (keep 2 after having trimmed 2):

\begin{itemize}
 \xintFor* #1 in {\xintKeep{2}{\xintTrim{2}{\xintCSVtoList{10,20,30,40,50}}}}
 \do 
  {\item (Item: #1)}
\end{itemize}


\clearpage
\section*{Selecting a subset of items from a list}

Selecting items 1, 3, 4, 2, 5, 5:

% for better efficiency, let's convert comma separated values
% in a "list" (in xinttools terminology) first, once and for all
% because \xintNthElt macro expects such as list

\oodef\MyListOfBracedItems{\xintCSVtoList{10,20,30,40,50}}

\begin{itemize}
% this uses the non-starred \xintFor, which accepts comma separated values
 \xintFor #1 in {1, 3, 4, 2, 5, 5}
 \do 
  {\item (Item: \xintNthElt{#1}{\MyListOfBracedItems})}
\end{itemize}

It is also possible to create an ``array'', for better efficiency afterwards:

\xintAssignArray\xintCSVtoList{10, 20, 30, 40, 50}\to\MyArray

\begin{itemize}
% this uses the starred \xintFor, which accepts braced items (or
% single tokens; spaces skipped)
 \xintFor* #1 in {1 3 4 2 5 5}
 \do 
  {\item (Item: \MyArray{#1})}
\end{itemize}


\end{document}

Page 1

enter image description here

Page 2

enter image description here

| improve this answer | |
  • I did not incorporate in my answer the fact that Python slicing notation is implemented in xintexpr: \xinttheexpr [10, 20, 30, 40, 50][2:4]\relax for example will expand to 30, 40. Indeed it will be usable only for (floating-point) numbers. One can also do \xinttheexpr subs(seq([L][i], i=0,2,3,1,4,4), L=10, 20, 30, 40, 50)\relax or \xinttheexpr seq([(10, 20, 30, 40, 50)][i], i=0,2,3,1,4,4)\relax but the former is more efficient and did not need the extra parenthesis pair to hide commas from seq. Syntax is nice, but works only with numbers. Completely expandable! – user4686 Sep 10 '18 at 16:31

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.