递归函数中的 renewcommand

递归函数中的 renewcommand

我正在尝试编写一个函数DoForList,它遍历以逗号分隔的列表并为每个项目调用一个处理程序函数。

其中一些处理函数会进行后续调用,从而DoForList覆盖\renewcommand现有\Handle函数,并且递归调用后的项目会传递给错误的函数。

\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}

这将打印

1 --1:一个、2:b、3:c、4:d
1:2、2:3、3:4、4:5

代替

1 --1:一个、2:b、3:c、4:d
2 --1:一个、2:b、3:c、4:d
3 --1:一个、2:b、3:c、4:d
4 --1:一个、2:b、3:c、4:d
5 --1:一个、2:b、3:c、4:d

\DoForList[\foobar]{1,2,3,4,5}发生的事情是\foobar{1}

此时,\HandleinDoForList被定义为调用\foobar

然后\foobar递归调用\DoForList[\foo]{a,b,c,d}

这重新定义并\Handle调用\foo{a}...。\foo{b}\foo{d}

\foo处理完所有项目 (a、b、c、d)后,\foobar应该使用下一个项目来调用\foobar{2},但实际上,\foo却使用剩余项目来调用(2,3,4,5)因为\Handle在递归调用中被重新定义,并且仍然将所有内容传递给\foo

clist_map_function可以很好地处理递归,但我无法使用它,因为我需要向传递额外的参数\foo

因此,我不想在递归调用中重新定义,而是\Handle想引入一个新命令\HandlerRecursion1HandlerRecursion2

我尝试使用它为每个递归级别\csname生成一个唯一的\Handle宏,但我是 Latex 的绝对初学者,并且有点不知道如何将该处理程序传递给 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}%
}

如果\csname\HandlerName\endcsname可以扩展并传递给\forcsvlist一切,那么一切都会顺利,但我不知道如何做到这一点。

答案1

LaTeX3 团队已经提供了您所需要的内容:

\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}

在此处输入图片描述

我不确定您的目的是什么,\foo并且\foobar用两个参数进行定义:作为参数给出的处理程序宏\DoForList应该只有一个。

答案2

您可以使用\csname将函数保存在具有唯一名称的临时变量中,然后从内部调用该变量,\Handler而不是直接调用#1

而不是打电话#1{##1}{\arabic{step}}%

您首先要保存#1一个变量

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

哪里recursion是计数递归级别的计数器。

你可以使用以下\Handler代码调用函数\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}

这将打印

1 --1:一个、2:b、3:c、4:d
2 --1:一个、2:b、3:c、4:d
3 --1:一个、2:b、3:c、4:d
4 --1:一个、2:b、3:c、4:d
5 --1:一个、2:b、3:c、4:d

相关内容