我尝试根据所见内容制作一个通用变量参数宏这里可以在每个参数前添加文本,在每个参数后附加文本,并在所有参数都经过后添加文本。
总的来说,这个方法似乎有效。不过,你不能在它后面放任何类型的文本,因为它会把事情搞乱:
% Allows using an @ in a macro name so we can use \@ifnextchar
\makeatletter
% Generic macro for doing things to variable arguments
% #1: What to prepend to the current item
% #2: What to append to the current item
% #3: What to put after all is done
\newcommand{\checknextarg}[3]{\@ifnextchar\bgroup{\@gobblenextarg{#1}{#2}{#3}}{#3}}
% Helper function for \checknextarg that controls the printing/displaying
% #1: What to prepend to the current item
% #2: What to append to the current item
% #3: What to put after all is done
% #4: The current item
\newcommand{\@gobblenextarg}[4]{#1#4#2 \checknextarg{#1}{#2}{#3}}
% Restores the @ symbol back to normal
\makeatother
假设我们有一个名为 func 的任意宏,它显示一些可变数量的参数。因此,如果我调用\func{a_1}{a_2}{a_3}
,它将显示类似
假设我希望 func 至少需要 2 个参数。因此,我尝试像这样添加函数:
\newcommand{\func}[2]{\text{func}(#1, #2\checknextarg{, }{}{)}}
第一个参数\checknextarg
是“,”因为我希望每个参数都用逗号分隔。第二个参数什么也没有,因为我不想在每个参数后面附加任何内容。第三个参数是“)”,用于结束该函数。
\func
对于 2 个或更多参数,此方法可正常工作。但是,由于 的性质,如果输入的命令少于 2 个,LaTeX 将在调用宏后尝试读取文本。\@ifnextchar
因此,我想知道是否有任何关于如何避免在这种情况下接受参数的想法。
答案1
我一点也不喜欢这种语法,原因在过去已经讨论过多次了。其中之一与宏的“可扩展性”有关。
是因为它可能。
问题 1:
除了用于将内容添加到参数前面/后面的递归机制(\checknextarg
/ \@checknextarg
/ \@gobblenextarg
)之外,您可能还需要另一种机制来递归收集和累积括号嵌套的参数。(在下面的例子中为\CollectAnotherArg
/ \@CollectAnotherArg
/ \@@CollectAnotherArg
/ 。)\RemoveBracesFromTwoArgs
问题 2:
\@ifnextchar
如 LaTeX 2ε 内核所定义,在执行“向前查看下一个字符”时,会不遵守并默默丢弃空格标记。如果下一个字符不是 -character {
,您可能不希望删除该字符之前的空格标记。如果在水平模式/不在数学模式下使用,则累积空格标记的机制可能会很有用。
把这些碎片放在一起,你可能会得到如下的结果:
\documentclass{article}
\usepackage{amsmath}
\makeatletter
%..............................................................................
% Mechanism for accumulating space-tokens and passing them towards something else:
%..............................................................................
\newcommand\@ifnextspace[2]{%
\def\reserved@a{#1}\def\reserved@b{#2}\futurelet\@let@token\@@ifnextspace
}%
\newcommand\@@ifnextspace{%
\ifx\@let@token\@sptoken\expandafter\reserved@a\else\expandafter\reserved@b\fi
}%
\newcommand\accumulatespacetokens[2]{%
% #1 Token-sequence where accumulated space-tokens are to be appended as another argument.
% #2 Space-tokens accumulated so far.
\@ifnextspace{\@gobblenextspacetoken{#1}{#2}}{#1{#2}}%
}%
\@ifdefinable\@gobblenextspacetoken{%
\long\def\@gobblenextspacetoken#1#2 {\accumulatespacetokens{#1}{#2 }}%
}%
%..............................................................................
% Generic macro for doing things to a variable amount of arguments:
%..............................................................................
\newcommand{\checknextarg}[3]{%
% #1: What to prepend to the current item.
% #2: What to append to the current item.
% #3: What to put after all is done.
% Let's accumulate space-tokens and append them to the third argument of the
% call to \kernel@ifnextchar.
\accumulatespacetokens{\@checknextarg{#1}{#2}{#3}}{}%
}%
\newcommand{\@checknextarg}[4]{%
% #1: What to prepend to the current item.
% #2: What to append to the current item.
% #3: What to put after all is done.
% #4: Space-tokens accumulated so far.
\kernel@ifnextchar\bgroup{\@gobblenextarg{#1}{#2}{#3}}{#3#4}%
}%
%
% Helper macro for \checknextarg that controls the printing/displaying:
%
\newcommand{\@gobblenextarg}[4]{%
% #1: What to prepend to the current item.
% #2: What to append to the current item.
% #3: What to put after all is done.
% #4: The current item.
#1#4#2\checknextarg{#1}{#2}{#3}%
}%
%..............................................................................
% Mechanism for accumulating brace-nested arguments and passing them into
% something else:
%..............................................................................
\newcommand\CollectAnotherArg[3]{%
% #1: What to do with list of brace-nested arguments accumulated so far in case there is no more brace-nested argument.
% #2: What to do with list of brace-nested arguments accumulated so far in case there is another brace-nested argument.
% #3: List of brace-nested arguments accumulated so far.
\accumulatespacetokens{\@CollectAnotherArg{#1}{#2}{#3}}{}%
}%
\newcommand\@CollectAnotherArg[4]{%
% #1: What to do with list of brace-nested arguments accumulated so far in case there is no more brace-nested argument.
% #2: What to do with list of brace-nested arguments accumulated so far in case there is another brace-nested argument.
% #3: List of brace-nested arguments accumulated so far.
% #4: Spaces accumulated so far.
\kernel@ifnextchar\bgroup{\@@CollectAnotherArg{#2}{#3}{#4}}{#1{#3#4}}%
}%
\newcommand\@@CollectAnotherArg[4]{%
% #1: What to do with list of brace-nested arguments accumulated so far in case there is another brace-nested argument.
% #2: List of brace-nested arguments accumulated so far.
% #3: Spaces accumulated so far.
% #4: Next brace-nested argument.
#1{#2#3{#4}}%
}%
\newcommand\RemoveBracesFromTwoArgs[2]{#1#2}%
%..............................................................................
% The macro \func:
%..............................................................................
\newcommand{\func}{%
% Nest as many
% \CollectAnotherArg{\@firstofone}{%
% ...
% }
% as you need.
\CollectAnotherArg{\@firstofone}{% <- at least one brace-grouped argument
\CollectAnotherArg{\@firstofone}{% <- at least two brace-grouped arguments
%\CollectAnotherArg{\@firstofone}{% <- at least three brace-grouped arguments
%\CollectAnotherArg{\@firstofone}{% <- at least four brace-grouped arguments
\text{func}(%
\RemoveBracesFromTwoArgs{%
\romannumeral0\expandafter\@gobble\@gobblenextarg{, }{}{)}%
}%
%}%
%}%
}%
}{}%
}%
\makeatother
\begin{document}
\verb*|\func ABCD|: \func ABCD
\verb*|\func {A}BCD|: \func {A}BCD
\verb*|\func {A} BCD|: \func {A} BCD
\verb*|\func {A}{B}CD|: \func {A}{B}CD
\verb*|\func {A}{B} CD|: \func {A}{B} CD
\verb*|\func {A} {B} CD|: \func {A} {B} CD
\verb*|\func {A}{B}{C}D|: \func {A}{B}{C}D
\verb*|\func {A} {B}{C}D|: \func {A} {B}{C}D
\verb*|\func {A}{B} {C} D|: \func {A}{B} {C} D
\verb*|\func {A} {B} {C} D|: \func {A} {B} {C} D
\verb*|\func {A}{B}{C}{D}|: \func {A}{B}{C}{D}
\verb*|$\func {a_1}{a_2}{a_3}$|: $\func {a_1}{a_2}{a_3}$
\end{document}