1

I'm trying to write a function DoForList that iterates over a comma seperated list and calls a handler function for each item.

Some of those handler functions do subsequent calls to DoForList thus \renewcommand overwrites the existing \Handle function, and the items after the recursive call get passed to the wrong function.

\documentclass{article}
\usepackage{etoolbox}% http://ctan.org/pkg/etoolbox
\usepackage{xparse}% http://ctan.org/pkg/etoolbox

\def\Handle{}
\newcommand{\PrintDebug}[2]{\typeout{DoForList: [#1] #2}}
\newcounter{step}
\NewDocumentCommand{\DoForList}{O{\PrintDebug}m}{%
   \setcounter{step}{0}
    \renewcommand{\Handle}[1]{%
        \stepcounter{step}
        %%%Call the handler function with <step>, <item>, <list>
        #1{##1}{\arabic{step}}%
    }
    \expandafter\forcsvlist\expandafter\Handle\expandafter{#2}%
     \setcounter{step}{0}
}

\newcommand{\foo}[2]{%
    \ifnum #2 = 1%
       \textit{#2:#1}%
    \else%
        , #2:#1%
     \fi%
}
\newcommand{\foobar}[2]{%
    #2  --\DoForList[\foo]{a,b,c,d}\\
}
\begin{document}
\DoForList[\foobar]{1,2,3,4,5} 
\end{document}

This prints

1 -- 1:a , 2:b , 3:c , 4:d
1:2 , 2:3 , 3:4 , 4:5

Instead of

1 -- 1:a , 2:b , 3:c , 4:d
2 -- 1:a , 2:b , 3:c , 4:d
3 -- 1:a , 2:b , 3:c , 4:d
4 -- 1:a , 2:b , 3:c , 4:d
5 -- 1:a , 2:b , 3:c , 4:d

What happens is that \DoForList[\foobar]{1,2,3,4,5} calls \foobar{1}

At this point, \Handle in DoForList is defined to call \foobar.

Then \foobar recursively calls \DoForList[\foo]{a,b,c,d}.

This redefines \Handle and which calls \foo{a}, \foo{b} ... \foo{d}.

When \foo processed all items (a,b,c,d), \foobar should be called with his next item \foobar{2}, but instead, \foo get's called with the remaining items (2,3,4,5) because \Handle got redefined in the recursive call, and still passes everything to \foo

clist_map_function handles recursion fine, but i can't use it because i need to pass extra arguments to \foo.

So instead of redefining \Handle in a recursive call, i would like to introduce a new command \HandlerRecursion1, HandlerRecursion2.

I tried using \csname to generate a unique \Handle macro for each level of recursion, but i'm an absolute beginner in Latex and a bit lost in how you would pass that handler to forcsvlist

\NewDocumentCommand{\DoForList}{O{\PrintDebug}mO{0}O{9999999}}{%
    \def\HandlerName{Handle\arabic{LevelOfRecursion}}
    \expandafter\renewcommand\expandafter{\csname\HandlerName\endcsname}[1]{...}
    %%% something like this?
    \expandafter\forcsvlist\expandafter -> \csname\HandlerName\endcsname <- \expandafter{#2}%
}

If \csname\HandlerName\endcsname could be expanded and passed to \forcsvlist everything would work fine, but i don't know how to do that.

  • This seems to be an XY-question; you have more chances to get a solution to your problem if you explain it clearly. Showing your attempt is good, but perhaps it's easier to look at the problem from a different point of view. – egreg Dec 14 '16 at 8:16
  • Again, you're trying to explain your attempt at a solution, but not the problem. – egreg Dec 14 '16 at 8:27
4

The LaTeX3 team has already provided what you're looking for:

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\DoForList}{O{\PrintDebug}m}
 {
  \clist_map_function:nN { #2 } #1
 }
\ExplSyntaxOff

\newcommand{\PrintDebug}[1]{#1}
\newcommand{\foo}[1]{#1:( }

\begin{document}

\DoForList[\foo]{a,b,c}

\end{document}

enter image description here

I'm not sure what your aim is with \foo and \foobar being defined with two arguments: the handler macro given as argument to \DoForList should have just one.

  • Thanks a lot! That looks promising and clean. The aim of the two arguments is to pass the index of the item in the list to\foo, in order to be able to e.g. treat the first item in the list differently or to break out of the recursion early. – C5H8NNaO4 Dec 14 '16 at 7:28
  • @C5H8NNaO4 Sorry, but I can't understand what you're after. – egreg Dec 14 '16 at 7:52
  • Thank you! I found a solution and added it as answer, i was thinking in the wrong direction. – C5H8NNaO4 Dec 14 '16 at 10:00
1

You can use \csname to save your function in a temporary variable with a unique name and call that from within \Handler instead of directly invoking #1

Instead of calling #1{##1}{\arabic{step}}%

You would first save #1 in a variable

\expandafter\def\csname Handler\arabic{recursion}\endcsname{#1}

Where recursion is a counter that counts the level of recursion.

Inside your \Handler you would call the function using \csuse

\csuse{Handler\arabic{recursion}}{##1}{\arabic{step}}%


\documentclass{article}
\usepackage{etoolbox}% http://ctan.org/pkg/etoolbox
\usepackage{xparse}% http://ctan.org/pkg/etoolbox

\def\Handle{}
\newcommand{\PrintDebug}[2]{\typeout{DoForList: [#1] #2}}
\newcounter{step}
\newcounter{recursion}
\setcounter{recursion}{0}

\NewDocumentCommand{\DoForList}{O{\PrintDebug}m}{%
    \setcounter{step}{0}  
    \stepcounter{recursion}
    \expandafter\def\csname Handler\arabic{recursion}\endcsname{#1}
    \renewcommand{\Handle}[1]{%
            \stepcounter{step}
            %%%Call the handler function with <step>, <item>, <list>
            \csuse{Handler\arabic{recursion}}{##1}{\arabic{step}}%
        }
    \expandafter\forcsvlist\expandafter\Handle\expandafter{#2}%
    \setcounter{step}{0}
    \addtocounter{recursion}{-1}
}

\newcommand{\foo}[2]{%
    \ifnum #2 = 1%
        \textit{#2:#1}%
    \else%
        , #2:#1%
    \fi%
}

\newcommand{\foobar}[2]{%
    #1  --\DoForList[\foo]{a,b,c,d}\\
}

\begin{document}
\DoForList[\foobar]{1,2,3,4,5} 
\end{document}

This prints

1 -- 1:a , 2:b , 3:c , 4:d
2 -- 1:a , 2:b , 3:c , 4:d
3 -- 1:a , 2:b , 3:c , 4:d
4 -- 1:a , 2:b , 3:c , 4:d
5 -- 1:a , 2:b , 3:c , 4:d

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.