6

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?

  • 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 8 '14 at 16:42
  • See e.g. tex.stackexchange.com/questions/140907/…: \foreach uses a group. – Joseph Wright Sep 8 '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 – user31729 Sep 8 '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 8 '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. – user31729 Sep 8 '14 at 16:51
8

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

  • 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 8 '14 at 16:52
  • 2
    @Caleb Your loops work unchanged if you use \pgfplotsforeachungrouped (requires \usepackage{pgfplots}) instead of \foreach. – egreg Sep 8 '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 8 '14 at 17:05
  • @Caleb With toggles it's easier, in my opinion. – egreg Sep 8 '14 at 17:23

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.