我正在尝试定义宏,它接受一个分隔列表并将每个元素传递给一个宏(该宏接受一个参数)。我有完整的工作版本,可以迭代
- 列表中的所有元素,
- 第一个 n,(其中 n 作为宏参数给出)但如果小于 n,则不添加空值,并且
- 前 n 个,但如果需要则添加空元素。
它们使用分组使所有定义都成为本地定义,但实际上在组结束后执行宏(这是必不可少的)。我已经用各种嵌套宏测试了它们,这些宏使用不同的分隔符再次调用它们,它们似乎很健壮。但是,当我尝试在另一个宏中使用它们时,我遇到了一个非常奇怪的问题,该宏应该采用列表的前 n 个元素(如果需要,末尾带有空元素)并调用接受 n 个参数的宏。如果我从上述宏的第 3 版复制粘贴大部分代码,一切都很好。但是,如果我尝试调用此宏,要求它为每个列表元素调用一个本地定义的宏,该宏应该将这些元素附加到(本地)标记列表中,则每次迭代后都会清空标记列表……我认为问题出在将标记附加到列表的辅助宏中,如下所示,但我找不到原因!它们在其他情况下按预期工作。我一定是错过了一些明显而愚蠢的东西,我很抱歉。以下是相关的宏:
\documentclass{article}
\usepackage{etoolbox}
\makeatletter
\newtoks\tmp@toks
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% append token #1 to token list #2
\def\append@tok{\@ifstar\@append@tok\@append@tok@wrapped}
\def\@append@tok@wrapped#1#2{% wrapped in braces
#2=\expandafter{\the#2{#1}}%
}%
\def\@append@tok#1#2{% as is
#2=\expandafter{\the#2#1}%
}%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% iterate over a list, up to #1 many elements, adding empty ones if needed
\def\apply@macro@to@fixed@list#1#2#3#4{%
% #1: exactly that many elements; add empty ones as needed,
% #2: macro of one argument, #3: list delimiter, #4: list
\begingroup%
\def\next@el##1##2#3##3\@nil{%
% ##1: "counter", ##2: next element, ##3: rest of list
\append@tok{#2{##2}}\tmp@toks%
\ifnum##1<#1\relax%
\ifstrempty{##3}{%
% call it with an empty element (delimiter)
\expandafter\next@el\number\numexpr ##1+1\relax #3\@nil%
}{%
\expandafter\next@el\number\numexpr ##1+1\relax ##3\@nil%
}%
\fi%
}%
\tmp@toks={}%
\next@el{1}#4#3\@nil%
\expandafter\endgroup\the\tmp@toks%
}
% call a macro with the first #1 elements of list
% THIS DOES NOT WORK
\def\apply@macro@to@split@list@A#1#2#3#4{%
% #1: exactly that many elements; add empty ones as needed,
% #2: macro of #1 number of argument, #3: list delimiter, #4: list
% for some reason this did not work... after exiting from \apply@macro@...
% the token list was empty (but was correctly appended to inside \save@tok))
\begingroup%
\def\save@tok##1{%
\append@tok{##1}\tmp@toks% does not append, simply reassigns
% not really sure how to display the content without using \meaning and a buffer macro
\expandafter\def\expandafter\@tmp\expandafter{\the\tmp@toks}%
token list is \texttt{\meaning\@tmp}\par%
}%
% idea is to put the macro to be called at the front of the token list
% then append its arguments; for debugging purposes, do not do that
% \tmp@toks={#2}%
\tmp@toks={}%
\apply@macro@to@fixed@list{#1}\save@tok{#3}{#4}%
exiting group, token list is \the\tmp@toks\par%
% and execute the macro outside the group
% \expandafter\endgroup\the\tmp@toks%
\endgroup%
}
\def\apply@macro@to@split@list@B#1#2#3#4{%
\begingroup%
\def\save@tok##1{%
\append@tok*{{##1}}\tmp@toks% does not append either, simply reassigns
\expandafter\def\expandafter\@tmp\expandafter{\the\tmp@toks}
token list is \texttt{\meaning\@tmp}\par%
}%
\tmp@toks={}%
\apply@macro@to@fixed@list{#1}\save@tok{#3}{#4}%
exiting group, token list is \the\tmp@toks\par%
\endgroup%
}
\begin{document}
% to see that nested calls to \apply@macro@to@fixed@list work:
\def\foo#1{%
% use a different delimiter
\apply@macro@to@fixed@list{3}\bar:{#1a:#1b:#1c:#1d}%
}
\def\bar#1{element is: #1\par}
\apply@macro@to@fixed@list{3}\foo,{a,b,c,d}%
%
\def\foo#1#2{FOO: #1, #2;\par}
% using unstarred \@append@tok, i.e. wraps it in braces
\apply@macro@to@split@list@A{2}\foo:{a:}
% using starred \@append@tok, I wrap it in braces before passing it to \append@tok
\apply@macro@to@split@list@B{2}\foo:{a:}
% now I redefine \append@tok to be exactly the same as its starred version
\def\append@tok#1#2{% as is
#2=\expandafter{\the#2#1}%
}%
% and pass it a wrapped token, as before
\def\apply@macro@to@split@list@C#1#2#3#4{%
\begingroup%
\def\save@tok##1{%
\append@tok{{##1}}\tmp@toks%
\expandafter\def\expandafter\@tmp\expandafter{\the\tmp@toks}
token list is \texttt{\meaning\@tmp}\par%
}%
\tmp@toks={}%
\apply@macro@to@fixed@list{#1}\save@tok{#3}{#4}%
exiting group, token list is \the\tmp@toks\par%
\endgroup%
}
% and it works
\apply@macro@to@split@list@C{2}\foo:{a:}
% it also works if I append the token without using a helper macro
\def\apply@macro@to@split@list@D#1#2#3#4{%
\begingroup%
\def\save@tok##1{%
\tmp@toks=\expandafter{\the\tmp@toks {##1}}%
\expandafter\def\expandafter\@tmp\expandafter{\the\tmp@toks}
token list is \texttt{\meaning\@tmp}\par%
}%
\tmp@toks={}%
\apply@macro@to@fixed@list{#1}\save@tok{#3}{#4}%
exiting group, token list is \the\tmp@toks\par%
\endgroup%
}
\apply@macro@to@split@list@D{2}\foo:{a:}
\end{document}
另外,我想知道我的一般方法和使用的技术是否合适?
答案1
这里,我引入了\feedtomama[]{}
,它以两种方式之一将参数传递给宏\mama
。如果没有提供可选参数,则解析列表并将每个项目传递给\mama
逐个传递给 。如果提供了可选参数,则将其视为项目数n在要传递给 的列表中\mama
,一次一个。如果n小于列表长度,则仅将该数量的列表元素传递给\mama
。如果n大于列表长度,则\mama
列表耗尽后将传递空元素,直到传递的元素总数 =n。
这里,\mama
仅仅将其参数设置在括号中。
列表分隔符可以用, 默认逗号listofitems
更改\setsepchar{}
,
\documentclass{article}
\usepackage{listofitems,pgffor}
\newcommand\feedtomama[2][\relax]{%
\ifx\relax#1\feedalltomama{#2}\else\feedNtomama{#1}{#2}\fi%
}
\newcommand\feedalltomama[1]{%
\readlist\z{#1}%
\foreachitem\zz\in\z[]{\expandafter\mama\expandafter{\zz}}%
}
\newcommand\feedNtomama[2]{%
\readlist\z{#2}%
\foreach\zz in {1,...,#1}{%
\ifnum\zz>\listlen\z[]\relax
\mama{}%
\else
\expandafter\mama\expandafter{\zz}%
\fi
}
}
\newcommand\mama[1]{(#1)}
\begin{document}
\feedtomama{1,2,3,4,5}
\feedtomama[7]{1,2,3,4,5}
\feedtomama[3]{1,2,3,4,5}
\end{document}