我想创建一个名为from\mycmd
的宏,并像这样使用它:\NewDocumentCommand
xparse
\mycmd-[options1]{action1}
\mycmd+[options2]{action2}
\mycmd/[options3]{action3}
xparse 强制参数“l”似乎是我想要的。但是,可选参数不能放在“l”参数附近(在我的情况下,它们是“-”、“+”、“/”),因为“l”是“读取直到第一个开放组标记的所有内容的参数:在标准 LATEX 中,这是一个左括号。”
那么,有没有更好的方法来解决这一问题呢?
答案1
使用t
说明符:
\documentclass{article}
\usepackage{xparse}
\NewDocumentCommand{\mycmd}{t- t+ t/ O{} m}{%
\IfBooleanTF{#1}
{called with -, options are #4, mandatory is #5}
{\IfBooleanTF{#2}
{called with +, options are #4, mandatory is #5}
{\IfBooleanTF{#3}
{called with /, options are #4, mandatory is #5}
{no symbol, options are #4, mandatory is #5}%
}%
}%
}
\begin{document}
\mycmd[ABC]{def}
\mycmd-[ABC]{def}
\mycmd+[ABC]{def}
\mycmd/[ABC]{def}
\end{document}
假设最多存在一个字符;如果给出多个字符,则按顺序使用-
、+
和中的第一个字符。/
答案2
使用标准命名法完成\newcommand
。
它依次调用两个宏。第一个宏吸收了第一个强制参数,我称之为“case”:+
、-
或/
,它将其保存在宏中。然后,第一个宏调用第二个宏,该宏吸收可选参数和操作参数。虽然我没有展示它,但可以使用类似 的测试对 case 执行分支测试\if+\mycase<codeA>\else<codeB>\fi
。
\documentclass{article}
\newcommand\mycmd[1]{\def\mycase{#1}\mycmdaux}
\newcommand\mycmdaux[2][]{Case is ``\mycase'', options are ``#1'' and action is ``#2''}
\begin{document}
\mycmd-[options1]{action1}
\mycmd+[options2]{action2}
\mycmd/[options3]{action3}
\end{document}
答案3
这个答案实际上并没有回答你的关于使用-package 来完成事情的问题xparse
。
相反,我们提出的是无需任何工具就能完成任务的方法xparse
。
如果不这样做,xparse
您可以使用\kernel@ifnextchar
for(不扩展地)检查输入流中的下一个标记的含义是否等于作为第一个参数提供的标记的含义,\kernel@ifnextchar
同时将下一个标记留在输入流中。
请注意,不会检查标记本身。只会检查其含义。
\mycmd
这可用于检查输入流中控制字标记后面的标记的含义是否是类别代码 1 的开括号字符的含义。
\mycmd
如果是,我们已经知道在正常的 catcode 制度下,输入流中控制字标记后面的标记不能是减号/加号/斜线。
如果不是,我们知道在正常的 catcode 制度下,\mycmd
输入流中控制字标记后面的标记可以作为无分隔符的宏参数进行处理,其中不能发生 catcode 1 和 2 标记对的括号剥离/剥离。
因此,在这种情况下,输入流中控制字标记后面的标记\mycmd
可以作为未定界宏参数获取,并且可以通过内部使用定界参数的完全可扩展宏来检查该未定界参数上的减号/加号/斜线。如果它既不是减号也不是加号也不是斜线,则可以将其放回。;-)
通过使用分隔宏参数,您可以扩展地(即,无需执行进一步的临时赋值)检查标记本身,而不仅仅是检查它们的含义。因此,您无法轻易欺骗机制,例如,通过使用除减号/加号/斜线之外的含义与类别代码 12 的减号/加号/斜线字符标记相同的标记。
请注意,下面的示例中考虑了周围的括号。
例如,\mycmd+...
将与区分开来\mycmd{+}...
。
进一步的操作将触发带有前导加号的操作。
当既没有前导减号,也没有前导加号,也没有前导斜杠且没有可选参数时,后者将触发所需的操作,并将加号作为(第一个)未限定/强制参数。
仅考虑第一个前导非空格标记。例如,\mycmd+-...
将触发以加号开头的所需操作,以减号开头的(第一个)未限定/强制参数。
请注意,在下面的示例中,我没有实现对输入流中控制字标记\mycmd
是否为右括号的检查:在问题中,处理可选参数和未限定宏参数
的条件是隐式给出的。 在正常的 catcode-régime 下,带有宏参数的未平衡右括号在任何情况下都会产生错误消息,因为括号需要与宏参数平衡。\mycmd
它还依赖于在正常 catcode-régime 下使用的宏,其中分配了类别代码 1 的唯一输入字符是开括号字符。
\documentclass{article}
\begingroup
\makeatletter
\@firstofone{%
\endgroup
%%-------------------------------------------------------------------------
%% Check whether argument is empty:
%%.........................................................................
%% \@CheckWhetherNull{<argument which is to be checked>}%
%% {<tokens to be delivered in case that
%% argument which is to be checked is empty>}%
%% {<tokens to be delivered in case that
%% argument which is to be checked is not empty>}%
\newcommand\@CheckWhetherNull[1]{%
\romannumeral0\expandafter\@secondoftwo\string{\expandafter
\@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
\@secondoftwo\string}\expandafter\@firstoftwo\expandafter{\expandafter
\@secondoftwo\string}\expandafter\expandafter\@firstoftwo{ }{}%
\@secondoftwo}{\expandafter\expandafter\@firstoftwo{ }{}\@firstoftwo}%
}%
%%-------------------------------------------------------------------------
%% Check whether argument contains no exclamation-mark on top-brace-level:
%%.........................................................................
%% \@CheckWhetherNoExclamationMark{<argument which is to be checked>}%
%% {<tokens to be delivered in case that
%% argument which is to be checked does not contain !>}%
%% {<tokens to be delivered in case that
%% argument which is to be checked does contain !>}%
\newcommand\@RemoveToExclamationMark{}%
\long\def\@RemoveToExclamationMark#1!{}%
\newcommand\@CheckWhetherNoExclamationMark[1]{%
\expandafter\@CheckWhetherNull\expandafter{\@RemoveToExclamationMark#1!}%
}%
%%-------------------------------------------------------------------------
%% Fork depending on minus, plus, slash, something else.
%%.........................................................................
%% (First via \kernek@ifnextchar check whether the next token is an
%% opening brace.
%% If it is, we know that it is neither a minus nor a plus nor a slash.
%% If it is not, it can be processed as an undelimited macro-argument
%% where under normal catcode-régime brace-stripping cannot occur, thus
%% in this ase use \mycmd@furthercheck for checking for
%% minus/plus/slash/something else.
%% Make the command robust as due to the not fully expandable
%% \kernel@ifnextchar it should not be carried out in
%% pure-expansion-contexts.)
\newcommand\mycmd{}%
\DeclareRobustCommand\mycmd{%
\kernel@ifnextchar\bgroup
{\mycmd@else}%<-Next token is opening brace or \bgroup,
% thus something other than minus/plus/slash.
{\mycmd@furthercheck}%<-Check for minus/plus/slash.
}%
%
\newcommand\@mycmdfork{}%
\long\def\@mycmdfork#1!!-!+!/!#2#3!!!!{#2}%
%
\newcommand\mycmd@furthercheck[1]{%
\@CheckWhetherNoExclamationMark{#1}{%
\@mycmdfork
!#1!-!+!/!{\mycmd@else#1}%<-Else #1 empty (actually this can't happen.)
!!#1!+!/!{\mycmd@minus}%<-minus
!!-!#1!/!{\mycmd@plus}%<-plus
!!-!+!#1!{\mycmd@slash}%<-slash
!!-!+!/!{\mycmd@else#1}%<-ELSE no exclamation-mark
!!!!%
}{\mycmd@else#1}%<-ELSE Exclamation-mark
}%
\newcommand\mycmd@minus[2][Optional argument's default at minus]{%
Case minus-sign:\\%
Optional argument is: \texttt{#1}\\%
Mandatory argument is: \texttt{#2}%
}%
\newcommand\mycmd@plus[2][Optional argument's default at plus]{%
Case plus-sign:\\%
Optional argument is: \texttt{#1}\\%
Mandatory argument is: \texttt{#2}%
}%
\newcommand\mycmd@slash[2][Optional argument's default at slash]{%
Case slash-sign:\\%
Optional argument is: \texttt{#1}\\%
Mandatory argument is: \texttt{#2}%
}%
\newcommand\mycmd@else[2][Optional argument's default at other cases]{%
Some other case:\\
Optional argument is: \texttt{#1}\\%
Mandatory argument is: \texttt{#2}%
}%
}%
\pagestyle{empty}
\begin{document}
\vspace*{-1.33in}%
\parskip=\baselineskip
\parindent=0pt
\enlargethispage{1in}%
\footnotesize
\verb|\mycmd X| yields:\\
\mycmd X
\verb|\mycmd!| yields:\\
\mycmd!
\verb|\mycmd{!}| yields:\\
\mycmd{!}
\verb|\mycmd{-}| yields:\\
\mycmd{-}
\verb|\mycmd{+}| yields:\\
\mycmd{+}
\verb|\mycmd{/}| yields:\\
\mycmd{/}
\verb|\mycmd{Some other case without given optional argument}| yields:\\
\mycmd{Some other case without given optional argument}
\verb|\mycmd[Given optional argument]{Some other case with given optional argument}| yields:\\
\mycmd[Given optional argument]{Some other case with given optional argument}
\verb|\mycmd+{Plus case without given optional argument}| yields:\\
\mycmd+{Plus case without given optional argument}
\verb|\mycmd+[Given optional argument]{Plus case with given optional argument}| yields:\\
\mycmd+[Given optional argument]{Plus case with given optional argument}
\verb|\mycmd-{Minus case without given optional argument}| yields:\\
\mycmd-{Minus case without given optional argument}
\verb|\mycmd-[Given optional argument]{Minus case with given optional argument}| yields:\\
\mycmd-[Given optional argument]{Minus case with given optional argument}
\verb|\mycmd/{Slash case without given optional argument}| yields:\\
\mycmd/{Slash case without given optional argument}
\verb|\mycmd/[Given optional argument]{Slash case with given optional argument}| yields:\\
\mycmd/[Given optional argument]{Slash case with given optional argument}
\end{document}
以防万一使用\mycmd
<optional-
或+
或/
> [optional]{mandatory}
,(第一个)强制参数始终嵌套在花括号中,可以根据大括号分隔的参数是否具有前导-
或+
或来实现完全可扩展的分叉/
:
\documentclass{article}
\begingroup
\makeatletter
\@firstofone{%
\endgroup
%%----------------------------------------------------------------------
%% Paraphernalia:
%%......................................................................
\newcommand\@Exchange[2]{#2#1}%
%%----------------------------------------------------------------------
%% Check whether argument is empty:
%%......................................................................
%% \@CheckWhetherNull{<argument which is to be checked>}%
%% {<tokens to be delivered in case that argument
%% which is to be checked is empty>}%
%% {<tokens to be delivered in case that argument
%% which is to be checked is not empty>}%
\newcommand\@CheckWhetherNull[1]{%
\romannumeral0\expandafter\@secondoftwo\string{\expandafter
\@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
\@secondoftwo\string}\expandafter\@firstoftwo\expandafter{\expandafter
\@secondoftwo\string}\expandafter\expandafter\@firstoftwo{ }{}%
\@secondoftwo}{\expandafter\expandafter\@firstoftwo{ }{}\@firstoftwo}%
}%
%%----------------------------------------------------------------------
%% Check whether argument's leading tokens form a specific token-sequence:
%%......................................................................
%% \@CheckWhetherLeadingTokens{<token sequence>}%
%% {<a single non space token that does
%% _not_ occur in <token sequence>>}%
%% {<internal token-check-macro>}%
%% {<argument which is to be checked>}%
%% {<tokens to be delivered in case <argument
%% which is to be checked> has <token sequence>
%% as leading tokens>}%
%% {<tokens to be delivered in case <argument
%% which is to be checked> does not have
%% <token sequence> as leading tokens>}%
\newcommand\@CheckWhetherLeadingTokens[4]{%
\romannumeral0\@CheckWhetherNull{#4}%
{\expandafter\expandafter\@firstoftwo{ }{}\@secondoftwo}%
{\expandafter\@secondoftwo\string{#3#2#4#1}{}}%
}%
\newcommand\@@CheckWhetherLeadingTokens[1]{%
\expandafter\@CheckWhetherNull\expandafter{\@firstoftwo{}#1}%
{\@Exchange{\@firstoftwo}}{\@Exchange{\@secondoftwo}}%
{\@Exchange{ }{\expandafter\expandafter\expandafter\expandafter
\expandafter\expandafter\expandafter}\expandafter\expandafter
\expandafter}\expandafter\@secondoftwo\expandafter{\string}%
}%
%%----------------------------------------------------------------------
%% \@internaltokencheckdefiner{<internal token-check-macro>}%
%% {<remover-macro>}%
%% {<token sequence>}%
%% - defines <internal token-check-macro> to snap everything
%% until reaching <token sequence>-sequence and pass that to
%% \@@CheckWhetherLeadingPhrase.
%% - defines <remover-macro> to gobble <token sequence>.
%% If <token sequence> is not there, this will raise an error.
%%......................................................................
\newcommand\@internaltokencheckdefiner[3]{%
\newcommand#1{}\long\def#1##1#3{\@@CheckWhetherLeadingTokens{##1}}%
\newcommand#2{}\long\def#2#3{}%
}%
\@internaltokencheckdefiner{\@CheckMinus}{\@gobbleminus}{-}%
\@internaltokencheckdefiner{\@CheckPlus}{\@gobbleplus}{+}%
\@internaltokencheckdefiner{\@CheckSlash}{\@gobbleslash}{/}%
%%----------------------------------------------------------------------
%% \mycmd<optional - or + or />[optional argument]{<mandatory argument>}
%%
%% !!! <mandatory argument> must be nested in curly braces !!!
%% !!! <mandatory argument> must be nested in curly braces !!!
%%......................................................................
\newcommand\mycmd{}%
% make \mycmd brace-delimited:
\long\def\mycmd#1#{%
\@CheckWhetherLeadingTokens{-}{.}{\@CheckMinus}{#1}{%
\expandafter\mycmd@minus\@gobbleminus
}{%
\@CheckWhetherLeadingTokens{+}{.}{\@CheckPlus}{#1}{%
\expandafter\mycmd@plus\@gobbleplus
}{%
\@CheckWhetherLeadingTokens{/}{.}{\@CheckSlash}{#1}{%
\expandafter\mycmd@slash\@gobbleslash
}{\mycmd@else}%
}%
}#1%
}%
\newcommand\mycmd@minus[2][Optional argument's default at minus]{%
Case minus-sign:\\%
Optional argument is: \texttt{#1}\\%
Mandatory argument is: \texttt{#2}%
}%
\newcommand\mycmd@plus[2][Optional argument's default at plus]{%
Case plus-sign:\\%
Optional argument is: \texttt{#1}\\%
Mandatory argument is: \texttt{#2}%
}%
\newcommand\mycmd@slash[2][Optional argument's default at slash]{%
Case slash-sign:\\%
Optional argument is: \texttt{#1}\\%
Mandatory argument is: \texttt{#2}%
}%
\newcommand\mycmd@else[2][Optional argument's default at other cases]{%
Some other case:\\
Optional argument is: \texttt{#1}\\%
Mandatory argument is: \texttt{#2}%
}%
}%
\begin{document}
\vspace*{-1.33in}%
\parskip=\baselineskip
\parindent=0pt
\enlargethispage{1in}%
\footnotesize
\verb|\mycmd{-}| yields:\\
\mycmd{-}
\verb|\mycmd{+}| yields:\\
\mycmd{+}
\verb|\mycmd{/}| yields:\\
\mycmd{/}
\verb|\mycmd{Some other case without given optional argument}| yields:\\
\mycmd{Some other case without given optional argument}
\verb|\mycmd[Given optional argument]{Some other case with given optional argument}| yields:\\
\mycmd[Given optional argument]{Some other case with given optional argument}
\verb|\mycmd+{Plus case without given optional argument}| yields:\\
\mycmd+{Plus case without given optional argument}
\verb|\mycmd+[Given optional argument]{Plus case with given optional argument}| yields:\\
\mycmd+[Given optional argument]{Plus case with given optional argument}
\verb|\mycmd-{Minus case without given optional argument}| yields:\\
\mycmd-{Minus case without given optional argument}
\verb|\mycmd-[Given optional argument]{Minus case with given optional argument}| yields:\\
\mycmd-[Given optional argument]{Minus case with given optional argument}
\verb|\mycmd/{Slash case without given optional argument}| yields:\\
\mycmd/{Slash case without given optional argument}
\verb|\mycmd/[Given optional argument]{Slash case with given optional argument}| yields:\\
\mycmd/[Given optional argument]{Slash case with given optional argument}
\end{document}