6

In following test:

\documentclass[spanish,10pt]{beamer}
\usepackage{pgfplots}

\begin{document}

\foreach \x in {2,4,...,12} {
   \x
}

\foreach \x in {2,4,...,4} {
   \x
}

\foreach \x in {2,4,...,2} {
   \x
}

\foreach \x in {2,4,...,-1} {
   \x
}

\end{document}

result is the following four lines:

2 4 6 8 10 12
2 4
2 4
2 4

first two lines are as expected but final two lines are not: I expected "2" and empty line (as usual in other programming languages).

Any hint to obtain these results? (in real source, the final number is an unknown argument).

  • it's a bit under documented but if you use the a,b,...,z form then I think you always get a,b and then a possibly empty iteration until the counter, stepping by b-a is at least z. – David Carlisle Nov 06 '16 at 16:56
  • 1
    I think \foreach processes each item in the list; it decides if it is numeric and, in this case it saves the difference with the previous item, if numeric, instead of the default 1; then it goes to the next; if it is ..., it goes on using the remembered difference until going beyond the next value (which should be numeric). It definitely does not parse the entire list before starting. – egreg Nov 06 '16 at 17:12
  • The pgf \foreach loop structure is meant for cases where there is something to 'do' based on a pattern: I suspect that the behaviour if you give defective input such as 2,4,...,-1 is undefined. – Joseph Wright Nov 06 '16 at 17:58
  • See http://tex.stackexchange.com/questions/327699/aligning-three-columns-of-equations for a macro that does what you explain in the addition. – egreg Nov 06 '16 at 20:14

2 Answers2

6

From the TikZ documentation (p.902)

Normally, when a list item ... is encountered, there should already have been two list items before it, which where numbers. Examples of numbers are 1, -10, or -0.24. Let us call these numbers x and y and let d := y − x be their difference. Next, there should also be one number following the three dots, let us call this number z.

In this situation, the part of the list reading “x,y,...,z” is replaced by “x, x + d, x + 2d, x + 3d, . . . , x + md,” where the last dots are semantic dots, not syntactic dots. The value m is the largest number such that x + md ≤ z if d is positive or such that x + md ≥ z if d is negative.

I think implicitly m is intended to be a positive integer, and for negative values of z formula is incorrectly described, (as noted in the comments) so in your last three cases, d=2 and m=1 for all cases, which leads to the sequence 2, 2+d= 2,4.

If you want to have a list that just contains (2) you can use:

\foreach \x in {2,...,2} {
   \x
}

I don't think there's a way to end up with a blank list, given the way the list syntax works.

As percusse notes in the comments, the first one or two elements of the list are always executed. To see this, here's a version of your code with a command added to display the value of the increment and the current value at each iteration. (These are lengths defined in pgffor.code.tex).

\documentclass{article}
\usepackage{pgffor}
\usepackage{xcolor}
\makeatletter
\newcommand\dbug{\textcolor{red}{\strip@pt\pgffor@iter;\strip@pt\pgffor@skip}\ }
\makeatother

\begin{document}

\foreach \x in {2,4,...,12} {
   \dbug\x
}

\foreach \x in {2,4,...,4} {
   \dbug\x
}

\foreach \x in {2,4,...,2} {
   \dbug\x
}

\foreach \x in {2,4,...,-1} {
   \dbug\x
}

\end{document}

formatted output of code

Alan Munn
  • 218,180
  • I don't think the description is really accurate and that no list is actually built up; otherwise, the case 2,4,...,-1 would only process 2. In this case m should be –2. – egreg Nov 06 '16 at 17:15
  • Please, where I can find the documentation? – pasaba por aqui Nov 06 '16 at 17:23
  • @pasabaporaqui The documentation is in the TikZ documentation. You can use texdoc tikz from the terminal to find it. – Alan Munn Nov 06 '16 at 17:25
  • @egreg The description in the documentation or my interpretation of it? I think m is required to be a positive integer. – Alan Munn Nov 06 '16 at 17:26
  • Probably documentation must change from "The value m is the largest ..." to "The value m is the maximum of 1 and the largest ..." – pasaba por aqui Nov 06 '16 at 17:27
  • @AlanMunn The description in the documentation is inaccurate – egreg Nov 06 '16 at 19:05
  • The first or first and second if given are inclusive. So no matter what the remaining is they are evaluated. End point is used for stopping condition. In the single start case for example since 1 is the increment {1,...,1} runs once but {0,1,...,1} runs twice – percusse Nov 06 '16 at 19:25
  • @percusse Yes, I've been playing around with the code and just figured that out. – Alan Munn Nov 06 '16 at 19:31
5

Apparently, \foreach \x in {<a>,<b>,...,<c>}{<code>} always executes the cycle with <a> and <b>; then it sees ... and decides what the difference between <a> and <b> is. Only at this point it starts a recursion which terminates when the next number exceeds <c> (in absolute value).

If your values are only integers, you can use expl3 that also has other benefits; for instance, you use #1 instead of \x and you need no trick for expansion; moreover, no group is opened.

\documentclass{article}

\usepackage{xparse}

\ExplSyntaxOn
% key-value form
\NewDocumentCommand{\xforeach}{mm}
 {
  \keys_set:nn { pasaba/xforeach }
   {
    start = 0,step = 1,end = 0,#1
   }
  \int_step_inline:nnnn
   { \l_pasaba_xforeach_start_int }
   { \l_pasaba_xforeach_step_int }
   { \l_pasaba_xforeach_end_int }
   { #2 }
 }
\keys_define:nn { pasaba/xforeach }
 {
  start .int_set:N = \l_pasaba_xforeach_start_int,
  step  .int_set:N = \l_pasaba_xforeach_step_int,
  end   .int_set:N = \l_pasaba_xforeach_end_int,
 }
% macro form
\NewDocumentCommand{\sforeach}{mmmm}
 {
  \int_step_inline:nnnn { #1 } { #2 } { #3 } { #4 }
 }
\ExplSyntaxOff

\begin{document}

\textbf{Key-value form}

$(2,2,12)$: \xforeach{start = 2,step = 2,end = 12}{#1 }

$(2,2,4)$: \xforeach{start = 2,step = 2,end = 4}{#1 }

$(2,2,2)$: \xforeach{start = 2,step = 2,end = 2}{#1 }

$(2,2,-1)$: \xforeach{start = 2,step = 2,end = -1}{#1 }

\bigskip

\textbf{Command form}

$(2,2,12)$: \sforeach{2}{2}{12}{#1 }

$(2,2,4)$: \sforeach{2}{2}{4}{#1 }

$(2,2,2)$: \sforeach{2}{2}{2}{#1 }

$(2,2,-1)$: \sforeach{2}{2}{-1}{#1 }

\end{document}

enter image description here

Now, suppose you have some \foreach in a macro, say

\newcommand{\foo}[1]{%
  \foreach \x in {2,4,...,#1}{do something with \x}%
}

You can turn it into a macro based on \sforeach with

\newcommand{\foo}[1]{%
  \sforeach{2}{2}{#1}{do something with ##1}%
}

The parameter #1 in \sforeach must become ##1, because it is used inside a macro definition.

egreg
  • 1,121,712
  • Of course this solution won't deal with all the other kinds of sequences that the TikZ version can: \foreach \x in {A,B,...,Z} {\x} which yields a letter sequence, or \foreach \x in {2^1, 2^,...,2^10}{$\x$} which typesets powers of two, etc. See page 902 of the docs if you want to extend your solution. ;-) – Alan Munn Nov 06 '16 at 22:26
  • @AlanMunn See my answer to the question I linked in a comment above: http://tex.stackexchange.com/questions/327699/aligning-three-columns-of-equations – egreg Nov 06 '16 at 22:41
  • @egreg: very nice solution. Are you so kind of add some more comments in the code? In particular, I do not see the real "body" of the new commands, that is, where they loop o calls a loop method to print all range elements. – pasaba por aqui Nov 07 '16 at 11:54
  • @pasabaporaqui The loop is \int_step_inline:nnnn {<start>}{<step>}{<end>}{<code>}, that's the power of expl3! – egreg Nov 07 '16 at 11:56
  • An improvement of this solution could be that \sforeach accepts any block of commands and can be used inside a \newcommand. I've edited original question to show some examples. – pasaba por aqui Nov 07 '16 at 12:35
  • @pasabaporaqui The addition should be a followup question. – egreg Nov 07 '16 at 12:56
  • @egreg: In the initial question it was said "in real source, the final number is an unknown argument", that means the loop is inside a "newcommand". In fact, it is only when the loop is inside a new command when the final value is unknown, causing the issues described. For this reason, I can not yet accept this solution as final answer, the problem is not solved. Sorry. – pasaba por aqui Nov 07 '16 at 19:13
  • @pasabaporaqui Just do \newcommand{\foo}[1]{\sforeach{2}{2}{#1}{whatever you want to do with ##1}} (you use ##1 where you would have \x in the \foreach loop). I added an example. – egreg Nov 07 '16 at 20:36