在思考如何通过测试区分一个命令的两个版本时,我想到了以下示例:
\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
包含可选参数数量(0
或1
)。对于 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
。