或者正如沃纳简化的那样:“如何在 xparse 中定义不带反斜杠的命令“。
背景:
我正在尝试设置一个宏来为我定义一个宏(它还将执行下面的 MWE 中不是必不可少的一些其他事情)。
\NumericalToNumber
下面的第一个版本通过传入来设置宏#1
。请注意\
名称中的。
\DefineMacro{\NumericalToNumber}{%
{one}{1}%
{two}{2}%
{three}{3}%
{four}{4}%
}%
这正是我想要的。
但是,要执行我需要在 中完成的“其他事情” \DefineMacro
,我需要提供不带反斜杠的宏名称。因此,我认为将调用更改为不带反斜杠\
的名称很容易,并使用csuse{}
从etoolbox
生成名称(而不是通常的\expandafter\csname...\endcsname
):
\DefineMacro{NumericalToNumber}{%
{one}{1}%
{two}{2}%
{three}{3}%
{four}{4}%
}%
但我似乎无法让这个版本运行。
我也尝试坚持使用第一个版本,并采用来自如何删除参数尾部的转义字符?但那对我来说不起作用。基本上,“其他事情”所需的代码已经可以运行,所以我更希望使用第二个版本。
代码:作品:\
使用\DefineMacro
\documentclass{article}
\usepackage{xparse}
\usepackage{xstring}
\NewDocumentCommand{\DefineMacro}{m m}{%
% #1 = command name to be defined (WITH backslash).
% #2 = pairs of {<property name>}{<propery value} (same as what \ItStrEqCase accepts)
\NewDocumentCommand{#1}{s O{UNKNOWN}}{%
\IfStrEqCase{##2}{%
#2%
}[\fbox{Unknown property name='##2' in \string#1.}]%
}%
}%
\DefineMacro{\NumericalToNumber}{%
{one}{1}%
{two}{2}%
{three}{3}%
{four}{4}%
}%
\begin{document}
\NumericalToNumber[two]
\NumericalToNumber[four]
\NumericalToNumber[ten]
\end{document}
\
代码:没有in则无法工作\DefineMacro
\documentclass{article}
\usepackage{etoolbox}
\usepackage{xparse}
\usepackage{xstring}
\NewDocumentCommand{\DefineMacro}{m m}{%
% #1 = command name to be defined (withOUT backslash).
% #2 = pairs of {<property name>}{<propery value} (same as what \ItStrEqCase accepts)
\NewDocumentCommand{\csuse{#1}}{s O{UNKNOWN}}{% <---- Use of \csuse here
\IfStrEqCase{##2}{%
#2%
}[\fbox{Unknown property name='##2' in \string#1.}]%
}%
}%
\DefineMacro{NumericalToNumber}{% <--- No backslash here in #1.
{one}{1}%
{two}{2}%
{three}{3}%
{four}{4}%
}%
\begin{document}
\NumericalToNumber[two]
\NumericalToNumber[four]
\NumericalToNumber[ten]
\end{document}
答案1
我不确定这是预期的结果,但是
\expandafter\NewDocumentCommand{\csname#1\endcsname}
不起作用,但是
\expandafter\NewDocumentCommand\csname#1\endcsname
确实如此。第一种方法尝试将宏的名称 (NumericalToNumber) 视为参数类型列表,从而导致有关未知“N”参数类型的错误。
\documentclass{article}
\usepackage{etoolbox}
\usepackage{xparse}
\usepackage{xstring}
\DeclareDocumentCommand{\DefineMacro}{mm}{%
% #1 = command name to be defined (withOUT backslash).
% #2 = pairs of {<property name>}{<propery value} (same as what \ItStrEqCase accepts)
\expandafter\DeclareDocumentCommand\csname#1\endcsname{s O{UNKNOWN}}{%
\IfStrEqCase{##2}{%
#2%
}[\fbox{Unknown property name='##2' in \string#1.}]%
}%
}
\DeclareDocumentCommand{\OtherDefineMacro}{mm}{%
% #1 = command name to be defined (withOUT backslash).
% #2 = pairs of {<property name>}{<propery value} (same as what \ItStrEqCase accepts)
\expandafter\NewDocumentCommand\csname#1\endcsname{s O{UNKNOWN}}{%
\IfStrEqCase{##2}{%
#2%
}[\fbox{Unknown property name='##2' in \string#1.}]%
}%
}
\DefineMacro{NumericalToNumber}{% <--- No backslash here in #1.
{one}{1}%
{two}{2}%
{three}{3}%
{four}{4}%
}%
\OtherDefineMacro{OtherNumericalToNumber}{% <--- No backslash here in #1.
{one}{1}%
{two}{2}%
{three}{3}%
{four}{4}%
}%
\begin{document}
\NumericalToNumber[two]
\NumericalToNumber[four]
\OtherNumericalToNumber[eight]
\NumericalToNumber[ten]
\end{document}
我也提供了一个版本\DeclareDocumentCommand
。
答案2
这是一个非常常见的问题:您必须在\NewDocumentCommand
开始其工作之前提供一个符号标记(宏名):
\expandafter\NewDocumentCommand\csname ...\endcsname
是解决方案;越复杂
\expandafter\NewDocumentCommand\expandafter{\csname ...\endcsname}
也可以。
有一种更巧妙的方法,可以避免这样做\expandafter
:
\documentclass{article}
\usepackage{xparse}
\newcommand{\SSErrorBox}[1]{\fbox{#1}}
\newcommand{\command}[1]{\texttt{#1}}
\ExplSyntaxOn
\NewDocumentCommand{\DefineMacro}{m m}
{
% #1 = command name to be defined (without backslash).
% #2 = pairs of {<property name>}{<property value>}
\grill_define_macro:cn { #1 } { #2 }
}
\cs_new_protected:Npn \grill_define_macro:Nn #1 #2
{
\NewDocumentCommand{#1}{s O{UNKNOWN}}
{
\str_case:nnF { ##2 } { #2 }
{ \SSErrorBox{Unknown~property~name="##2"~in~"\command{\string#1}".} }
}
}
\cs_generate_variant:Nn \grill_define_macro:Nn { c }
\ExplSyntaxOff
\DefineMacro{NumericalToNumber}{% <--- No backslash here in #1.
{one}{1}
{two}{2}
{three}{3}
{four}{4}
}
\begin{document}
X\NumericalToNumber[two]X
X\NumericalToNumber[four]X
X\NumericalToNumber[ten]X
\end{document}
宏\DefineMacro
只是将控制权传递给\grill_define_macro:cn
。然后我们定义\grill_define_macro:Nn
;根据 中的约定expl3
,此函数需要两个参数:一个无括号的单个标记(N
)和一个通常带括号的参数(n
)。
参数#1
是要定义的宏\NewDocumentCommand
,#2
是成对的列表。我们可以使用,{<key>}{<value>}
而不是,它被称为\IfStrEqCase
\str_case:nnF
\str_case:nnF
{ ##2 } % the second argument to the macro
{ #2 } % the list of properties
{ ... } % what to do when there's no match
当然,\IfStrEqCase
如果您愿意,您可以自由使用,只需记住,在编程环境中\ExplSyntaxOn
/\ExplSyntaxOff
空格会被忽略,并且必须将所需空格提供为~
。
\grill_define_macro:cn
最后,定义了变体;神奇的事情就在这里发生:c
意味着必须用大括号括起来参数,并从中构建一个符号标记前 \grill_define_macro:Nn
被执行。
我使用了一些 X 来表明没有伪造的空间潜入。
\NewDocumentCommand
这里是的定义xparse.sty
:
\cs_new_protected:Npn \NewDocumentCommand #1#2#3
{
\cs_if_exist:NTF #1
{
\__msg_kernel_error:nnx { xparse } { command-already-defined }
{ \token_to_str:N #1 }
}
{ \__xparse_declare_cmd:Nnn #1 {#2} {#3} }
}
如果这是
\cs_new_protected:Npn \xparse_new_document_command:Nnn #1#2#3
{
\cs_if_exist:NTF #1
{
\__msg_kernel_error:nnx { xparse } { command-already-defined }
{ \token_to_str:N #1 }
}
{ \__xparse_declare_cmd:Nnn #1 {#2} {#3} }
}
\cs_set_eq:NN \NewDocumentCommand \xparse_new_document_command:Nnn
你可以在你的文档中简单地说,
\ExplSyntaxOn
\cs_generate_variant:Nn \xparse_new_document_command:Nnn { c }
\cs_set_eq:NN \NewDocumentCommandName \xparse_new_document_command:cnn
\ExplSyntaxOff
并且您可以使用不\NewDocumentCommandName{<name>}{<args>}{<code>}
带<name>
反斜杠的宏名称。
可能有充分的理由不在包中执行此操作。
不过,你可以自己做:
\ExplSyntaxOn
\cs_set_eq:NN \grill_new_document_command:Nnn \NewDocumentCommand
\cs_generate_variant:Nn \grill_new_document_command:Nnn { c }
\cs_set_eq:NN \NewDocumentCommandName \grill_new_document_command:cnn
\ExplSyntaxOff
然而,我更喜欢上面概述的策略。
答案3
我以为标准\cs_generate_variant:Nn
当不可用时(如本例),方法是使用\exp_args:??
。在这种情况下
\ExplSyntaxOn
\NewDocumentCommand \DefineMacro { }
{ \exp_args:Nc \NewDocumentCommand }
\ExplSyntaxOff
或者,没有必要\ExplSyntax..
\NewDocumentCommand\DefineMacro{}
{\csname exp_args:Nc\endcsname\NewDocumentCommand}
%{\csuse{exp_args:Nc}\NewDocumentCommand}