Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

I have the following nested loop situation:

\begingroup
  \def\do##1{%
    \begingroup
      \def\do####1{%
         \csletcs{somemacro@##1@####1}{someothermacro@##1@####1}}%
      \dolistloop\somelista
    \endgroup}%
  \dolistloop\somelistb
\endgroup

where I need to have the \csletcs definition exist outside of the two groups but not globally. There is a nice trick from Martin Scharrer in a comment to this question:

How do I get a value out of a group?

but it doesn't work in a loop like this as putting the \endgroups in the inner loop really mess the next loop iterations up. Is there any way (etoolbox is ok) to get the \csletcs definitions out of the groups without breaking further iterations of the loops? I assume I need the groups otherwise the do loop arguments don't reference properly.

Just for completeness, here is an attempted solution using Martin Sharrer's trick:

\begingroup
  \def\do##1{%
    \begingroup
      \def\do####1{%
         \edef\@z{\endgroup\endgroup\csletcs{somemacro@##1@####1}{someothermacro@##1@####1}}\@z}%
      \dolistloop\somelista
    \endgroup}%
  \dolistloop\somelistb
\endgroup

This works for the first nested iteration and the resulting cs is defined properly outside of the groups. Unfortunately, the groups are then closed and the next iterations are a real mess.

share|improve this question
    
Perhaps you could make it global within the group, access it outside as desired and then \undef it with one of the solutions from How to undo a \def (i.e., Need a \undef capability). So then it no longer exists globally. –  Peter Grill Apr 5 '13 at 18:50
    
I thought of that but how do I make it global within the group without first \defing or \leting etc. outside of the inner groups? Bear in mind that I need the \do loop argument values ##1 and ####1 in the definitions and they are only available inside the two groups so I don't have the information to define the cs outside of the groups ... –  PLK Apr 5 '13 at 18:55
    
As far as I can see, the group is used for redefining \do in order to process the "inner list"; you can use whatever command you want for processing a list, see \forlistloop. –  egreg Apr 5 '13 at 19:59

2 Answers 2

up vote 8 down vote accepted

Your example is a little incomplete (I had to make some changes to get it to compile), but the gist of the answer is just to collect your definitions into a token list (set globally) and then expand the list after the second group (causing local definitions).

\documentclass{article}
\usepackage{etoolbox}
\begin{document}
\makeatletter
\def\somelista{x}
\def\somelistb{y}

\texttt{\meaning\somemacro@y@x}
\def\someothermacro@y@x{a}
\begingroup
  \def\do#1{%
    \begingroup
    \def\do##1{%
      \global\toks1=\expandafter{\the\toks1 \csletcs{somemacro@#1@##1}{someothermacro@#1@##1}}}%
    \dolistloop\somelista
    \endgroup}%
  \dolistloop\somelistb
\endgroup
\the\toks1
\texttt{\meaning\somemacro@y@x}

\makeatother
\end{document}

Gives: "undefined macro:->a" as the typeset output.

share|improve this answer
    
Or was I supposed to use \toks1 for global definitions? I forget. –  Ryan Reich Apr 5 '13 at 19:03
    
Great, thanks. I had not really looked at token registers - this is a new area for me. But it works. I tried to make it neater using \addto@hook but this seems to give me errors but I'll look at that. –  PLK Apr 5 '13 at 19:16
1  
To be honest, the token register is unnecessary. One could also use macros, for which I think a simple \gappto would suffice. –  Ryan Reich Apr 5 '13 at 19:17
    
Ah, right, that's a good point, I can try that. –  PLK Apr 5 '13 at 19:19
1  
@RyanReich Yes, it's better to use \toks1; I've amended it and also added a space before \csletcs that otherwise would be expanded, which is not desirable. –  egreg Apr 5 '13 at 22:56

You can use whatever handler you want for processing a list using \forlistloop instead of \dolistloop; so you don't need to redefine \do in a group not to clobber the outer loop definition: just use two different handlers.

\documentclass{article}
\usepackage{etoolbox}

\newcommand\processlistb[1]{%
  \def\temp{#1}%
  \forlistloop\processlista\somelista
}
\newcommand\processlista[1]{%
  \csletcs{somemacro@\temp @#1}{someothermacro@\temp @#1}%
  \csshow{somemacro@\temp @#1}%
}

\listadd\somelista{a}
\listadd\somelista{b}
\listadd\somelista{c}

\listadd\somelistb{x}
\listadd\somelistb{y}

% Just to add some mock definitions
\csdef{someothermacro@x@a}{xa}
\csdef{someothermacro@x@b}{xb}
\csdef{someothermacro@x@c}{xc}
\csdef{someothermacro@y@a}{ya}
\csdef{someothermacro@y@b}{yb}
\csdef{someothermacro@y@c}{yc}

% Do the iterations
\forlistloop\processlistb\somelistb

\stop

The relevant part of the log file:

> \somemacro@x@a=macro:
->xa.
<recently read> \somemacro@x@a

l.29 \forlistloop\processlistb\somelistb

?
> \somemacro@x@b=macro:
->xb.
<recently read> \somemacro@x@b

l.29 \forlistloop\processlistb\somelistb

?
> \somemacro@x@c=macro:
->xc.
<recently read> \somemacro@x@c

l.29 \forlistloop\processlistb\somelistb

?
> \somemacro@y@a=macro:
->ya.
<recently read> \somemacro@y@a

l.29 \forlistloop\processlistb\somelistb

?
> \somemacro@y@b=macro:
->yb.
<recently read> \somemacro@y@b

l.29 \forlistloop\processlistb\somelistb

?
> \somemacro@y@c=macro:
->yc.
<recently read> \somemacro@y@c

l.29 \forlistloop\processlistb\somelistb

?
share|improve this answer
    
I did try this before using \dolistloop but I have problems as the macro that contains this is embedded in a pile of stuff which also runs in loops etc. I may try it again but I have it working with \dolistloop at the moment –  PLK Apr 5 '13 at 20:15

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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