我正在编写一个包,我需要获取宏中的参数数量。比如说,用户必须定义一个宏
\def\usermacro#1#2....
然后他打电话
\dosomestuffdefinedinmypackage
并且此命令\usermacro
根据其参数的数量以某种方式获取和使用它,所以我需要找到一种方法来了解它。
最好不要使用其他软件包,我想要了解它是如何工作的,而不是学习咒语。谢谢!
答案1
下面定义了一个\getnumargs
接受两个参数的宏。第一个是任意宏,第二个是应该存储结果的宏。
它会将第一个宏所采用的参数数量存储在第二个宏中。
警告:这只会获取该宏将采用的参数数量,不会多也不会少。如果宏采用分隔参数,它也会失败。其中包括一些从技术上讲是正确的示例,但很可能不会返回您想要的内容。
\documentclass[]{article}
\makeatletter
\edef\getnumargs@ifmacro
{%
\noexpand\expandafter\noexpand\getnumargs@ifmacro@
\noexpand\getnumargs@meaning\relax\noexpand\getnumargs@ifmacro@true
\detokenize{macro:}\relax\noexpand\getnumargs@ifmacro@false
}
\def\getnumargs@ifmacro@true#1\getnumargs@ifmacro@false#2{#2}
\expandafter\def\expandafter\getnumargs@ifmacro@
\expandafter#\expandafter1\detokenize{macro:}#2\relax
{}
\newcommand*\getnumargs@ifmacro@false[1]
{%
\PackageError{Alex}{}{Not a macro!}%
\def\getnumargs@num{0}%
}
\newcommand\getnumargs[2]
{%
\begingroup
\edef\getnumargs@meaning{\meaning#1}%
\getnumargs@ifmacro
{%
\expandafter\getnumargs@settmp\expandafter{\getnumargs@meaning}%
\getnumargs@getnum
}%
\expandafter
\endgroup
\expandafter\def\expandafter#2\expandafter{\getnumargs@num}%
}
\newcommand\getnumargs@settmp[1]
{%
\expandafter\def\expandafter\getnumargs@tmp
\getnumargs@getargs#1\relax##1##2##3\relax{##2}%
}
\expandafter\def\expandafter\getnumargs@getargs
\expandafter#\expandafter1\detokenize{macro:}#2->#3\relax
{#2}
\edef\getnumargs@getnum
{%
\edef\noexpand\getnumargs@num
{%
\noexpand\the\noexpand\numexpr
\noexpand\getnumargs@tmp
\string#1\string#2\string#3%
\string#4\string#5\string#6%
\string#7\string#8\string#9%
.{10}\relax
-1\relax
}%
}
\makeatother
\newcommand\noargs{}
\newcommand\onearg [1]{}
\newcommand\twoargs [2]{}
\newcommand\threeargs[3]{}
\newcommand\fourargs [4]{}
\newcommand\fiveargs [5]{}
\newcommand\sixargs [6]{}
\newcommand\sevenargs[7]{}
\newcommand\eightargs[8]{}
\newcommand\nineargs [9]{}
\begin{document}
\getnumargs\section\tmp
\tmp
\getnumargs\texttt\tmp
\tmp
\getnumargs\emph\tmp
\tmp
\getnumargs\noargs\tmp
\tmp
\getnumargs\onearg\tmp
\tmp
\getnumargs\twoargs\tmp
\tmp
\getnumargs\threeargs\tmp
\tmp
\getnumargs\fourargs\tmp
\tmp
\getnumargs\fiveargs\tmp
\tmp
\getnumargs\sixargs\tmp
\tmp
\getnumargs\sevenargs\tmp
\tmp
\getnumargs\eightargs\tmp
\tmp
\getnumargs\nineargs\tmp
\tmp
\end{document}
答案2
灵感来自user202729 的回答,这里有一个更简单的编码:
\documentclass{article}
\ExplSyntaxOn
\NewExpandableDocumentCommand{\getnumargs}{m}
{
\int_eval:n { \str_count:e { \cs_argument_spec:N #1 } / 2 }
}
\cs_generate_variant:Nn \str_count:n { e }
\ExplSyntaxOff
\newcommand\noargs{}
\newcommand\onearg [1]{}
\newcommand\twoargs [2]{}
\newcommand\threeargs[3]{}
\newcommand\fourargs [4]{}
\newcommand\fiveargs [5]{}
\newcommand\sixargs [6]{}
\newcommand\sevenargs[7]{}
\newcommand\eightargs[8]{}
\newcommand\nineargs [9]{}
\begin{document}
\getnumargs\texttt
\getnumargs\emph
\getnumargs\noargs
\getnumargs\onearg
\getnumargs\twoargs
\getnumargs\threeargs
\getnumargs\fourargs
\getnumargs\fiveargs
\getnumargs\sixargs
\getnumargs\sevenargs
\getnumargs\eightargs
\getnumargs\nineargs
\texttt{\edef\test{\getnumargs\nineargs}\meaning\test}
\end{document}
所需扩展步骤数大于 2,但这并不重要。想法是,如果步骤数为 1,很好,我们可以根据o
需要将其用作参数说明符;否则使用e
。