有没有可靠的方法来获取命令的参数数量?

有没有可靠的方法来获取命令的参数数量?

在思考如何通过测试区分一个命令的两个版本时,我想到了以下示例:

\documentclass{article}
\begin{document}
\def\testA#1{\#2->blabla}
\typeout{\meaning\testA}
\def\testB#1->\#2{blabla}
\typeout{\meaning\testB}
\end{document}

日志中的输出是

macro:#1->\#2->blabla
macro:#1->\#2->blabla

因此,我认为它无法用来\meaning计算命令的参数数量。还有其他方法吗(不执行命令)?

答案1

注意:这仅适用于通过 定义的参数数量\newcommand,而不适用于通过 定义的参数数量\def。即便如此,它也仅适用于新定义的\newcommand,而不适用于内置于 LaTeX 中的参数。这可能不是问题,因为那些内置命令都有详尽的文档。

但请注意,按照序言设置加载的包将使用记住的信息进行处理。因此,我的 MWE 可以并且确实告诉我有关已加载的stackengine包宏的参数。

\newcommand*已编辑,也可用于调用。

我确信重新定义\newcommand通常是一件非常糟糕的事情,但我基本上重新定义它以保存参数数量和可选参数的值,因为我将定义提供给已保存的版本\newcommand

\x对于使用 创建的宏\newcommand,宏\xARGS包含参数数量,并\xOPTARGS包含可选参数数量(01)。对于 Ulrike 的观点,这些提供参数计数的额外宏在执行宏之前可用(但显然在\newcommand定义它的 之后)。

已编辑以表明它也适用于\csname定义。

\documentclass{article}
\let\SVnewcommand\newcommand
\makeatletter
\renewcommand\newcommand{\@ifstar%
  {\gdef\StarVer{T}\newcommandaux}%
  {\gdef\StarVer{F}\newcommandaux}%
}
\def\newcommandaux#1{\saverootname{#1}\futurelet\testchar\MaybeHasArgsCom}
\def\saverootname#1{\xdef\Mname{\expandafter\@gobble\string#1}}
%
\def\MaybeHasArgsCom{\ifx[\testchar \let\next\HasArgsCom
  \else%
    \expandafter\xdef\csname\Mname ARGS\endcsname{0}%
    \expandafter\xdef\csname\Mname OPTARGS\endcsname{0}%
    \if T\StarVer%
      \let\next\HasNoArgsStarCom%
    \else%
      \let\next\HasNoArgsCom%
    \fi%
  \fi%
  \next}
\def\HasArgsCom[#1]{%
  \xdef\SaveArgs{#1}%
  \expandafter\xdef\csname\Mname ARGS\endcsname{#1}%
  \futurelet\testchar\MaybeHasOptArgCom%
}
\def\MaybeHasOptArgCom{%
  \ifx[\testchar%
    \expandafter\xdef\csname\Mname OPTARGS\endcsname{1}%
    \if T\StarVer%
      \let\next\HasOptArgStarCom%
    \else
      \let\next\HasOptArgCom%
    \fi%
  \else%
    \expandafter\xdef\csname\Mname OPTARGS\endcsname{0}%
    \if T\StarVer%
      \let\next\HasNoOptArgStarCom%
    \else
      \let\next\HasNoOptArgCom%
    \fi%
  \fi%
  \expandafter\next\expandafter[\SaveArgs]}
%
\long\def\HasOptArgCom[#1][#2]#3{%
  \expandafter\SVnewcommand\csname\Mname\endcsname[#1][#2]{#3}%
}
\def\HasOptArgStarCom[#1][#2]#3{%
  \expandafter\SVnewcommand\expandafter*\csname\Mname\endcsname[#1][#2]{#3}%
}
\long\def\HasNoOptArgCom[#1]#2{%
  \expandafter\SVnewcommand\csname\Mname\endcsname[#1]{#2}%
}
\long\def\HasNoOptArgStarCom[#1]#2{%
  \expandafter\SVnewcommand\expandafter*\csname\Mname\endcsname[#1]{#2}%
}
\long\def\HasNoArgsCom#1{%
  \expandafter\SVnewcommand\csname\Mname\endcsname{#1}%
}
\long\def\HasNoArgsStarCom#1{%
  \expandafter\SVnewcommand\expandafter*\csname\Mname\endcsname{#1}%
}
%
\makeatother
\parskip 1em
\usepackage[T1]{fontenc}
\usepackage{lmodern,stackengine}
\begin{document}
\newcommand*\XYZ{xyz}
\string\XYZ, whose value is \XYZ, 
has \XYZARGS{} arguments
and \XYZOPTARGS{} optional arguments.

\newcommand\PDQ[1]{p#1d#1q}
\detokenize{\PDQ{123\par123}}, whose value is \PDQ{123\par123}, has \PDQARGS{} argument(s)
and \PDQOPTARGS{} optional arguments.

\newcommand*\ggg[2]{g#1g#2gx}
\string\ggg\{1\}\{2\}, whose value is \ggg{1}{2}, has \gggARGS{} argument(s)
and \gggOPTARGS{} optional arguments.

\newcommand\mytest[1][Q]{#1}
\string\mytest, whose value is \mytest, has \mytestARGS{} argument(s)
and \mytestOPTARGS{} optional arguments.

\string\mytest[X], whose value is \mytest[X], has \mytestARGS{} argument(s)
and \mytestOPTARGS{} optional arguments.

\newcommand\othertest[3][I]{([#1]#2,#3)}
\string\othertest\{j\}\{k\}, whose value is \othertest{j}{k}, has 
\othertestARGS{} argument(s) and \othertestOPTARGS{} optional arguments.

\string\othertest[i]\{j\}\{k\}, whose value is \othertest[i]{j}{k}, has 
\othertestARGS{} argument(s) and \othertestOPTARGS{} optional arguments.

\expandafter\newcommand\csname X1\endcsname[1]{*#1*}
\string\csname X1\string\endcsname\{OOO\}, whose value is 
  \csname X1\endcsname{OOO}, has \csname X1ARGS\endcsname{} argument(s) and 
  \csname X1OPTARGS\endcsname{} optional arguments.

\string\stackengine{} has \stackengineARGS{} arguments 

\string\stackon{} has \stackonARGS{} arguments and \stackonOPTARGS{} optional argument.

\end{document}

在此处输入图片描述

如果还想保存是否\newcommand使用星号版本调用,则\newcommandaux可以修改为

\def\newcommandaux#1{%
  \saverootname{#1}%
  \expandafter\xdef\csname\Mname STAR\endcsname{\StarVer}%
  \futurelet\testchar\MaybeHasArgsCom%
}

那么,a\newcommand\x...将被\xSTAR定义为F,而 的调用\newcommand*\x...\xSTAR被定义为T

相关内容