我正在使用以下生成器来生成可变宏:
% USAGE :: \VARIADIC{name}{start}{mid}{stop}
%
\newcommand{\VARIADIC}[4]{%
\expandafter\newcommand\csname GobbleNext#1Arg\endcsname[1]{%
#3##1\csname CheckNext#1Arg\endcsname%
}%
\expandafter\newcommand\csname CheckNext#1Arg\endcsname{%
\csname @ifnextchar\endcsname\bgroup{\csname GobbleNext#1Arg\endcsname}{#4\endgroup}%
}%
\expandafter\newcommand\csname #1\endcsname[2]{%
\begingroup#2##1#3##2\csname CheckNext#1Arg\endcsname%
}%
}
本质上,它检查是否有更多的“参数”——以\bgroup
ie开头的标记\{
,然后决定是否递归扩展或停止。
我的使用方法如下:
\VARIADIC {List} {[} {;} {]}
\VARIADIC {Sum} {(} {+} {)}
...
\begin{document}
\List {a} {b} {c} {d} {e}
\Sum {x} {y} {z}
\end{document}
我还可以应用简单的样式,例如:
\VARIADIC {Sum} {\textbf\bgroup(} {+} {)\egroup}
但我不确定这是否是最佳/正确的方法。我用了\ensuremath
很多,但下面的宏不起作用——我不知道为什么……
\VARIADIC {Tuple} {\ensuremath\bgroup\langle} {,} {\rangle\egroup}
我希望能够\VARIADIC
轻松地使用现有宏编写宏。具体来说,我正在寻找一种方法来编写任意现有的一元宏,如下所示\XYZ
:
\VARIADIC {Test} {\XYZ\bgroup(} {,} {)\egroup}
答案1
您的定义存在问题,因为某些宏要求其参数位于显式括号中。在您的\Tuple
示例中,\ensuremath
将读取以下参数\bgroup
并确保此参数在数学模式下设置。但这不是您想要的。(我认为该\textbf
示例也恰好因为此宏的定义方式而起作用。)
解决方案是,您可以\VARIADIC
通过一个新参数扩展宏#2
,然后将新定义的命令以 的形式包装到输出流中的所有材料。由于将开括号和闭括号拆分到不同的宏中非常棘手,因此我们不直接输出宏的材料,而是将其收集在另一个由和#2{...}
传递的参数中。最后,当未找到新参数时,所有收集的材料都用 进行排版。\GobbleNext...Arg
\CheckNext...Arg
#2
如果您不想修改最终组,您可以将(新的)第二个参数留空。为组插入的显式括号将替换\begingroup ... \endgroup
旧定义的括号。
\documentclass{article}
\newcommand{\VARIADIC}[5]{%
\expandafter\newcommand\csname GobbleNext#1Arg\endcsname[2]{%
\csname CheckNext#1Arg\endcsname{##1#4##2}%
% Material moved to new argument ^^^^^^^^
}%
\expandafter\newcommand\csname CheckNext#1Arg\endcsname[1]{%
\csname @ifnextchar\endcsname\bgroup{\csname GobbleNext#1Arg\endcsname{##1}}{#2{##1#5}}%
% Wrapper macro #2 with new explicit braces ---------------------------------^-^-----^
}%
\expandafter\newcommand\csname #1\endcsname[2]{%
\csname CheckNext#1Arg\endcsname{#3##1#4##2}%
% Material moved to new argument ^^^^^^^^^^
}%
}
\VARIADIC {List} {} {[} {;} {]}
\VARIADIC {Sum} {\textbf} {(} {+} {)}
\VARIADIC {Tuple} {\ensuremath} {\langle} {,} {\rangle}
\begin{document}
\List {a} {b} {c} {d} {e}
\Sum {x} {y} {z}
\Tuple {\alpha} {\beta} {\gamma}
\end{document}
输出
答案2
不应使用具有可变参数数量的宏。您可以改为传递逗号分隔的列表,这更容易输入。
\documentclass{article}
\usepackage{amsmath}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\variadic}{mm}
{
\group_begin:
\keys_set:nn { saswat/variadic } { #2 }
\saswat_variadic_define:nVVVV { #1 }
\l__saswat_variadic_env_tl % optional envelope
\l__saswat_variadic_start_tl % start
\l__saswat_variadic_mid_tl % mid
\l__saswat_variadic_stop_tl % stop
\group_end:
}
\keys_define:nn { saswat/variadic }
{
env .tl_set:N = \l__saswat_variadic_env_tl,
start .tl_set:N = \l__saswat_variadic_start_tl,
mid .tl_set:N = \l__saswat_variadic_mid_tl,
stop .tl_set:N = \l__saswat_variadic_stop_tl,
env .initial:n = \use:n,
}
\seq_new:N \l__saswat_variadic_items_seq
\cs_new_protected:Nn \saswat_variadic_define:nnnnn
{
\cs_new_protected:cpn { #1 } ##1
{
\seq_set_split:Nnn \l__saswat_variadic_items_seq { , } { ##1 }
#2 { #3 \seq_use:Nn \l__saswat_variadic_items_seq { #4 } #5 }
}
}
\cs_generate_variant:Nn \saswat_variadic_define:nnnnn { nVVVV }
\ExplSyntaxOff
\variadic{List}{
start=[,
mid=;,
stop=],
}
\variadic{Sum}{
env=\textbf,
start=(,
mid=+,
stop=),
}
\variadic{Tuple}{
env=\ensuremath,
start=\langle,
mid={,},
stop=\rangle,
}
\begin{document}
\List{a,b,c,d,e}
\Sum{x,y,z}
\Tuple{a,b,\dots,z}
\end{document}
我选择了键值接口,因此不需要记住参数的精确顺序,因为键可以按任何顺序指定。
关键env
在于“封装宏”,请参阅示例。如果没有指定,则默认为\use:n
本质上无操作,因为它仅传递其参数。