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.

Without using \global, the following code:

\documentclass{minimal}
\usepackage[T1]{fontenc}
\usepackage{tikz}
\usepackage{etextools}

\newcommand\appendbyforeach[2]{%
  \AfterGroup*{\noexpand\AfterGroup{\noexpand\edef\noexpand#1{\noexpand#1#2}}}
}

\newcommand\defincbyforeach[2]{%
  \AfterGroup*{\noexpand\def\noexpand#1{#2}}
  \AfterGroup*{\noexpand\AfterGroup{\noexpand\def\noexpand#1{#2}}}
}

\newcommand\meaningwithname[1]{\textbackslash#1: \expandafter\meaning\csname #1\endcsname}

\newcommand\mymacro{
  \begingroup
  \def\myarray{init}
  \def\valmax{0}
  \foreach \i in {1,8,-3}{
    \appendbyforeach{\myarray}{,\i}
    \pgfmathsetmacro{\max}{\valmax < \i ? \i : \valmax}
    \defincbyforeach\valmax{\max}
    %%% print state of \myarray and \valmax
    foreach \i\\\meaningwithname{myarray}\\\meaningwithname{valmax}\par
  }
  inside\\\meaningwithname{myarray}\\\meaningwithname{valmax}\par
  \endgroup
  outside\\\meaningwithname{myarray}\\\meaningwithname{valmax}\par
}

\begin{document}\mymacro\end{document}

produces a document containing:

    foreach 1
\myarray: macro:->init
\valmax: macro:->0
    foreach 8
\myarray: macro:->init
\valmax: macro:->1.0
    foreach -3
\myarray: macro:->init
\valmax: macro:->8.0
    inside
\myarray: macro:->init,1,8,-3
\valmax: macro:->8.0
    outside
\myarray: \relax
\valmax: \relax

This result suits me well!

But is there a better way to define macros \appendbyforeach and \defincbyforeach while achieving the same effects?

Does etextools package is really practical here?

share|improve this question
    
I could just leave the question here and add my solution as an answer. Would it be better? –  Paul Gaborit Aug 30 '12 at 16:41
    
Is the problem finding the max of an array with a locally defined macro? –  percusse Aug 30 '12 at 17:32
    
@percusse This is just one example of usage of \defincbyforeach. The idea is to calculate (for each iteration and after the loop), $u_n = f(u_ {n-1}, x_1, x_2, ...)$ where $x_1,x_2,...$ are the variables of the foreach loop. –  Paul Gaborit Aug 30 '12 at 19:41
add comment

1 Answer

up vote 3 down vote accepted

Note: This code always uses etextools. Before accepting my answer to my own question, I hope that others will offer me more interesting solutions (without etextools, more effective, faster, simpler...). But always without using \global!

Edit: Here a new version without expanded values and with only two macros.

  1. \afteriterationdef to (re)define a macro after current iteration.

  2. \afterforeachdef to (re)define a macro after foreach (and after iteration).

Here is the code of these two macros:

\documentclass{minimal}
\usepackage[T1]{fontenc}
\usepackage{tikz}
\usepackage{etextools}

\makeatletter

% define a macro after current iteration
\newcommand\afteriterationdef[1]{\aftergroup@def#1}

% define a macro after foreach (and after iteration)
\newcommand\afterforeachdef[1]{\afteriterationdef{#1}\AfterGroup{\aftergroup@def#1}}

\makeatother

And an example:

\newcommand\meaningwithname[1]{%
  \textbackslash#1: \expandafter\meaning\csname #1\endcsname}

\newcommand\mymacro{
  {
    \def\myarray{init}
    \def\myvalue{1}
    \foreach \i in {1,8,-3,-1}{
      % append some value to \myarray
      \edef\myarray{\myarray,\i}
      \afterforeachdef\myarray
      % incremental definition of \mtvalue
      \pgfmathsetmacro{\myvalue}{\myvalue*\i}
      \afterforeachdef\myvalue
      %%% print meaning of \myarray and \myvalue at each iteration
      foreach \i: \meaningwithname{myarray}, \meaningwithname{myvalue}\par
    }
    %%% print meaning of \myarray and \myvalue after \foreach
    inside group: \meaningwithname{myarray}, \meaningwithname{myvalue}\par
  }
  %%% print meaning of \myarray and \myvalue outside
  outside group: \meaningwithname{myarray}, \meaningwithname{myvalue}\par
}

\begin{document}\mymacro\end{document}

This example produces this text:

foreach 1: \myarray: macro:->init,1, \myvalue: macro:->1.0
foreach 8: \myarray: macro:->init,1,8, \myvalue: macro:->8.0
foreach -3: \myarray: macro:->init,1,8,-3, \myvalue: macro:->-24.0
foreach -1: \myarray: macro:->init,1,8,-3,-1, \myvalue: macro:->24.0
inside group: \myarray: macro:->init,1,8,-3,-1, \myvalue: macro:->24.0
outside group: \myarray: \relax, \myvalue: \relax

Edit 2: Another example with two nested \foreach:

\newcommand\mymacrowithnestedforeach{
  {
    \def\myarray{init}
    \def\myvalue{1}
    \foreach \i in {1,...,3}{
      \foreach \j in {1,...,3}{
        % append some value to \myarray
        \edef\myarray{\myarray,(\i,\j)}
        \afterforeachdef\myarray
        % incremental definition of \myvalue
        \pgfmathsetmacro{\myvalue}{\myvalue+\j*\i}
        \afterforeachdef\myvalue
        %%% print meaning of \myarray and \myvalue at each iteration
        int foreach \i: \meaningwithname{myarray}, \meaningwithname{myvalue}\par
      }
      % 
      \afterforeachdef\myarray
      \afterforeachdef\myvalue
      %%% print meaning of \myarray and \myvalue at each iteration
      ext foreach \i: \meaningwithname{myarray}, \meaningwithname{myvalue}\par
    }
    %%% print meaning of \myarray and \myvalue after \foreach
    inside  group: \meaningwithname{myarray}, \meaningwithname{myvalue}\par
  }
  %%% print meaning of \myarray and \myvalue outside
  outside group: \meaningwithname{myarray}, \meaningwithname{myvalue}\par
}

\begin{document}\mymacrowithnestedforeach\end{document}

This example produces this text:

int foreach 1: \myarray: macro:->init,(1,1), \myvalue: macro:->2.0
int foreach 1: \myarray: macro:->init,(1,1),(1,2), \myvalue: macro:->4.0
int foreach 1: \myarray: macro:->init,(1,1),(1,2),(1,3), \myvalue: macro:->7.0
ext foreach 1: \myarray: macro:->init,(1,1),(1,2),(1,3), \myvalue: macro:->7.0
int foreach 2: \myarray: macro:->init,(1,1),(1,2),(1,3),(2,1), \myvalue: macro:->9.0
int foreach 2: \myarray: macro:->init,(1,1),(1,2),(1,3),(2,1),(2,2), \myvalue: macro:->13.0
int foreach 2: \myarray: macro:->init,(1,1),(1,2),(1,3),(2,1),(2,2),(2,3), \myvalue: macro:->19.0
ext foreach 2: \myarray: macro:->init,(1,1),(1,2),(1,3),(2,1),(2,2),(2,3), \myvalue: macro:->19.0
int foreach 3: \myarray: macro:->init,(1,1),(1,2),(1,3),(2,1),(2,2),(2,3),(3,1), \myvalue: macro:->22.0
int foreach 3: \myarray: macro:->init,(1,1),(1,2),(1,3),(2,1),(2,2),(2,3),(3,1),(3,2), \myvalue: macro:->28.0
int foreach 3: \myarray: macro:->init,(1,1),(1,2),(1,3),(2,1),(2,2),(2,3),(3,1),(3,2),(3,3), \myvalue: macro:->37.0
ext foreach 3: \myarray: macro:->init,(1,1),(1,2),(1,3),(2,1),(2,2),(2,3),(3,1),(3,2),(3,3), \myvalue: macro:->37.0
inside group: \myarray: macro:->init,(1,1),(1,2),(1,3),(2,1),(2,2),(2,3),(3,1),(3,2),(3,3), \myvalue: macro:->37.0
outside group: \myarray: \relax, \myvalue: \relax
share|improve this answer
    
This will fail for nested groups and nested \foreach. See the discussion at tex.stackexchange.com/questions/56294/…. –  Ahmed Musa Sep 2 '12 at 2:40
    
@AhmedMusa This solution works with nested \foreach (I added an example to my answer). If you define a macro into a group into a \foreach, you should first call \aftergroup@def into this group then call \afterforeachdef outside this group and into the \foreach. –  Paul Gaborit Sep 2 '12 at 7:32
add comment

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.