我正在尝试编写一个函数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}
此时,\Handle
inDoForList
被定义为调用\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
想引入一个新命令\HandlerRecursion1
。HandlerRecursion2
我尝试使用它为每个递归级别\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