7

I have a several rather lengthy documents with numbered points (catechism questions) that I would like to be able to selectively include in other documents using a utility function that uses a range argument. I ran into trouble and eventually traced it to the way the \foreach seems to run in its own private scope of some kind. The loops seem to iterate properly, but things set inside them (such as \newif definitions or toggles from the etoolbox package don't seem to stick.

This fails miserably:

\documentclass{scrartcl}
\usepackage{pgffor}
\usepackage{etoolbox}

\newcommand{\wkk}[1]{%
    \foreach \x in {1,...,5}{%
        \providetoggle{wkk\x}
    }
    \foreach \x in {#1}{%
        \toggletrue{wkk\x}
    }
    % In a real example the following content would be \input here
    \iftoggle{wkk1}{output 1 }{}
    \iftoggle{wkk2}{output 2 }{}
    \iftoggle{wkk3}{output 3 }{}
    \iftoggle{wkk4}{output 4 }{}
    \iftoggle{wkk5}{output 5 }{}
}

\begin{document}

\wkk{2,...,4}

\end{document}

Yet if I replace the \foreach loops with exactly what they should be doing in the above example, it works fine:

\documentclass{scrartcl}
\usepackage{pgffor}
\usepackage{etoolbox}

\newcommand{\wkk}[1]{%
    \providetoggle{wkk1}
    \providetoggle{wkk2}
    \providetoggle{wkk3}
    \providetoggle{wkk4}
    \providetoggle{wkk5}
    \toggletrue{wkk2}
    \toggletrue{wkk3}
    \toggletrue{wkk4}
    % In a real example the following content would be \input here
    \iftoggle{wkk1}{output 1 }{}
    \iftoggle{wkk2}{output 2 }{}
    \iftoggle{wkk3}{output 3 }{}
    \iftoggle{wkk4}{output 4 }{}
    \iftoggle{wkk5}{output 5 }{}
}

\begin{document}

\wkk{2,...,4}

\end{document}

Obviously these are kind of clumsy MWE's and I would be including the content from an external source file that was marked up with the correct toggles.

What am I doing wrong with the pgffor package? Or is there a conceptually better way to do this?

Mico
  • 506,678
Caleb
  • 1,988
  • 2
    See section 83 of the PGF manual: Note that in each execution of commands the commands are put in a TEX group. This means that local changes to counters inside commands do not persist till the next iteration – daleif Sep 08 '14 at 16:42
  • See e.g. http://tex.stackexchange.com/questions/140907/whats-the-difference-between-the-various-methods-for-producing-for-loops: \foreach uses a group. – Joseph Wright Sep 08 '14 at 16:42
  • \iftoggle does not survive any loop style I know -- I detected that too -- unfortunately. I think, there is always a grouping within loops, for some \ifthenelse queries, whatever –  Sep 08 '14 at 16:42
  • Based on comments, I need a different way to do this conceptually. Can that be answered in this question as is or do I need to edit it? – Caleb Sep 08 '14 at 16:47
  • @daleif: \setcounter is not group-safe, or am I completely confused? So at least LaTeX - counters are not safe from being changed in a group. –  Sep 08 '14 at 16:51
  • \setcounter is global. – daleif Sep 08 '14 at 16:53

1 Answers1

9

Each cycle of \pgffor is performed in a group, so the definition of the toggles and the setting don't survive them.

One can solve the issue with \toggletrue by writing \global\toggletrue, but there's no “global \providetoggle”. However you can define your own:

\let\gprovidetoggle\providetoggle
\patchcmd{\gprovidetoggle}{\cslet}{\global\cslet}{}{}

Example:

\documentclass{scrartcl}
\usepackage{pgffor}
\usepackage{etoolbox}

\let\gprovidetoggle\providetoggle
\patchcmd{\gprovidetoggle}{\cslet}{\global\cslet}{}{}

\newcommand{\wkk}[1]{%
    \foreach \x in {1,...,5}{%
        \gprovidetoggle{wkk\x}%
        \global\togglefalse{wkk\x}% set it to false
    }%
    \foreach \x in {#1}{%
        \global\toggletrue{wkk\x}%
    }%
    % In a real example the following content would be \input here
    \iftoggle{wkk1}{output 1 }{}%
    \iftoggle{wkk2}{output 2 }{}%
    \iftoggle{wkk3}{output 3 }{}%
    \iftoggle{wkk4}{output 4 }{}%
    \iftoggle{wkk5}{output 5 }{}%
}

\begin{document}

\wkk{2,...,4}

\end{document}

enter image description here

egreg
  • 1,121,712
  • My original attempt at this used \expandafter\newif\csname ifwkk\x\endcsname etc. which makes the markup in the source file a little easier. I switched to etoolbox toggles when I couldn't make that go. Is that potentially a slightly simpler route to go than this one? If so how do I make those \if definitions global? – Caleb Sep 08 '14 at 16:52
  • 2
    @Caleb Your loops work unchanged if you use \pgfplotsforeachungrouped (requires \usepackage{pgfplots}) instead of \foreach. – egreg Sep 08 '14 at 16:57
  • \pgfplotsforeachungrouped makes my original code using \newifs work perfectly. Would you mind adding a note about that to this answer? – Caleb Sep 08 '14 at 17:05
  • @Caleb With toggles it's easier, in my opinion. – egreg Sep 08 '14 at 17:23