我将通过一个例子来解释我的意思。
\documentclass{article}
\usepackage{arrayjobx}
\newcounter{foo}
\setcounter{foo}{0}
\renewcommand{\bar}{\addtocounter{foo}{1}Some text}
\newarray\baz
% -------------------
\begin{document}
\baz(1)={\bar}
\arabic{foo} % foo should be 1
\baz(1), \arabic{foo} % foo should still be 1
\baz(1), \arabic{foo} % foo should once again be 1
\end{document}
当我将命令分配\bar
给数组时,它不会扩展,而是原封不动地分配给数组,这意味着计数器foo
保持为零,直到我调用\baz(1)
。
是否可以使其\bar
在分配给数组时立即扩展\baz
,以便计数器只增加一次,并且的值\baz(1)
仅仅等于Some text
?
答案1
下面添加了两个宏\addtoarraywithcounter
和\addtoarrayasbox
。前者可以处理\addtocounter
参数中的无括号(如果参数只有一个标记,例如单个宏,则在处理之前将参数展开一次)。
后者非常短(8 行代码)。它可以处理几乎任意的宏内容,并且只将排版结果包含在数组中。它通过在框内排版参数并将框存储在数组内来实现这一点。不在全局范围内起作用的代码不会对排版框之外产生任何影响(在下面的示例中,我在 的定义\def\baz{hihi}
中添加了\bar
,以表明这不会改变 的定义\baz
)。然而,LaTeX\addtocounter
全局设置计数器,因此它会影响 第三个参数之外的代码\addtoarrayasbox
。
\documentclass{article}
\usepackage{arrayjobx}
\newcounter{foo}
\setcounter{foo}{0}
\renewcommand{\bar}{\addtocounter{foo}{1}Some text\def\baz{hihi}}
\newarray\baz
% -------------------
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand \addtoarraywithcounter { m m +m }
{
% if only one token is handed in and that is not a space
\bool_if:nTF
{ \tl_if_single_token_p:n { #3 } && \tl_if_head_is_N_type_p:n { #3 } }
{
% expand that token, it might be a macro
\noibe_array_with_counter:nno { #1 } { #2 } { #3 }
}
{
% seems to be a more than a single token
\noibe_array_with_counter:nnn { #1 } { #2 } { #3 }
}
}
\cs_new_protected:Npn \noibe_array_with_counter:nnn #1 #2 #3
{
\tl_if_in:nnTF { #3 } { \addtocounter }
{
\noibe_handle_addtocounter:nnw { #1 } { #2 } \q_nil #3 \q_stop
}
{
\noibe_add_to_array:nnn { #1 } { #2 } { #3 }
}
}
\cs_new_protected:Npn
\noibe_handle_addtocounter:nnw #1 #2 #3 \addtocounter #4 #5
{
\addtocounter { #4 } { #5 }
\noibe_handle_addtocounter_auxi:nnow { #1 } { #2 } { \use_none:n #3 } \q_nil
}
\cs_new_protected:Npn \noibe_handle_addtocounter_auxi:nnnw #1 #2 #3 #4 \q_stop
{
\noibe_handle_addtocounter_auxii:nnno
{ #1 } { #2 } { #3 } { \use_none:n #4 }
}
\cs_generate_variant:Nn \noibe_handle_addtocounter_auxi:nnnw { nnow }
\cs_new_protected:Npn \noibe_handle_addtocounter_auxii:nnnn #1 #2 #3 #4
{
\noibe_add_to_array:nnn { #1 } { #2 } { #3 #4 }
}
\cs_generate_variant:Nn \noibe_handle_addtocounter_auxii:nnnn { nnno }
\cs_new_protected:Npn \noibe_add_to_array:nnn #1 #2 #3
{
#1 ( #2 ) = { #3 }
}
\cs_generate_variant:Nn \noibe_array_with_counter:nnn { nno }
\NewDocumentCommand \addtoarrayasbox { m m m }
{
\cs_if_exist:cF { l__noibe_array_ \cs_to_str:N #1 _content_ #2 _box }
{ \box_new:c { l__noibe_array_ cs_to_str:N _content_ #2 _box } }
\hbox_set:cn { l__noibe_array_ cs_to_str:N _content_ #2 _box } { #3 }
#1 ( #2 ) =
{ \hbox_unpack:c { l__noibe_array_ cs_to_str:N _content_ #2 _box } }
}
\ExplSyntaxOff
\begin{document}
\addtoarrayasbox\baz{1}{\bar}
\addtoarraywithcounter\baz{2}{{hihi}\addtocounter{foo}{1}{haha}}
%\baz(1)={\bar}
\arabic{foo} % foo should be 2
\baz(1), \baz(2), \arabic{foo} % foo should still be 2
\baz(1), \baz(2), \arabic{foo} % foo should once again be 2
\end{document}