我有一个命令,它接受可选参数作为键值对,由l3keys
包处理。我使用\begingroup
和键来处理仅为当前命令调用设置的选项\endgroup
。\keys_set:nn
但是,此命令应使用 使最终用户可以使用某些全局文档命令\NewDocumentCommand
。由于分组,这将只是本地命令。但是,\global
定义之前的 会导致错误
! You can't use a prefix with `\begingroup'.
<to be read again>
\group_begin:
l.93 ... = H, description = ab fg.]{Abfg}{Ab_{fg}}
? h
I'll pretend you didn't say \long or \outer or \global or \protected.
?
看来这不是有意的。
以下是一个最小(非)工作示例,未使用分组,因此覆盖了设置的键。预期的分组和全局定义已在注释中标记,添加这些行会产生上述错误。
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\keys_define:nn { categories }
{
key .tl_set:N = \l__category_key_tl,
key .default:n = \c_novalue_tl
% More keys ommited
}
\cs_generate_variant:Nn\tl_if_novalue:nF{ V F }
\NewDocumentCommand{\DeclareCategory}{ O{} m}
{
% \begingroup
\keys_set:nn{ categories }{#1}
% \exp_args:NNc\global\NewDocumentCommand{#2}{ }
\exp_args:Nc\NewDocumentCommand{#2}{ }
{
%Do some stuff here with #2:
called:~#2,~
% Do some other stuff, using the \l__category_key_tl token list
Value~is:~\tl_use:N\l__category_key_tl
}
% \endgroup
}
\ExplSyntaxOff
\begin{document}
\DeclareCategory[ key = foo]{A}
\DeclareCategory[ key = foo2]{B}
\A
\B
\end{document}
我怎样才能在小组内做出这样的全局定义?
答案1
这里有两个问题:
\NewDocumentCommand
不是全局声明;- 您想要使用为替换文本中的键设置的值,而不是令牌列表名称。
第一个问题可以通过首先将键设置为“标准状态”来解决,从而避免对组的需求。
第二个问题通过扩展定义“控制扩展”来解决。这里我使用了组策略,并且\cs_new_protected:cpx
全局起作用。
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\keys_define:nn { categories }
{
key .tl_set:N = \l__category_key_tl,
key .default:n = \c_novalue_tl
% More keys omitted
}
\prg_generate_conditional_variant:Nnn \tl_if_novalue:n { V } { p, T, F, TF }
\NewDocumentCommand{\DeclareCategory}{ O{} m}
{
\keissler_category_define:nn { #1 } { #2 }
}
\cs_new_protected:Nn \keissler_category_define:nn
{
\group_begin:
\keys_set:nn { categories }{#1}
\cs_new_protected:cpx { #2 }
{
\exp_not:n
{
%Do some stuff here with #2:
called:~#2,~Value~is:~
}
\exp_not:V \l__category_key_tl
}
\group_end:
}
\ExplSyntaxOff
\begin{document}
\DeclareCategory[key = foo]{A}
\DeclareCategory[key = foo2]{B}
\A
\B
\end{document}
一种不同的方法,当您拥有少量密钥时可以使用。
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\keys_define:nn { categories }
{
key .tl_set:N = \l__category_key_tl,
key .default:n = \c_novalue_tl
% More keys omitted
}
\prg_generate_conditional_variant:Nnn \tl_if_novalue:n { V } { p, T, F, TF }
\NewDocumentCommand{\DeclareCategory}{ O{} m }
{
\group_begin:
\keys_set:nn { categories } { #1 }
\keissler_category_define:nnV { #1 } { #2 } \l__category_key_tl
\group_end:
}
\cs_new_protected:Nn \keissler_category_define:nnn
{
\cs_new_protected:cpn { #2 }
{
%Do some stuff here with #2:
called:~#2,~Value~is:~#3
}
}
\cs_generate_variant:Nn \keissler_category_define:nnn { nnV }
\ExplSyntaxOff
\begin{document}
\DeclareCategory[ key = foo]{A}
\DeclareCategory[ key = foo2]{B}
\A
\B
\end{document}
答案2
你只需要将材料“偷运”出组即可。只需一个设置:
\NewDocumentCommand{\DeclareCategory}{ O{} m}
{
\group_begin:
\keys_set:nn { categories } {#1}
\exp_args:NNNV \group_end:
\tl_set:Nn \l__category_key_tl \l__category_key_tl
\exp_args:Nc\NewDocumentCommand{#2}{ }
{
%Do some stuff here with #2:
called:~#2,~
% Do some other stuff, using the \l__category_key_tl token list
Value~is:~\tl_use:N\l__category_key_tl
}
}
有了更多,您可以将材料保存在草稿中tl
,然后使用相同的方法将其移出组。
如果你想要价值而不是变量,你需要安排一个间接
\NewDocumentCommand{\DeclareCategory}{ O{} m}
{
\group_begin:
\keys_set:nn { categories } {#1}
\exp_args:NNNV \group_end:
\tl_set:Nn \l__category_key_tl \l__category_key_tl
\exp_args:NnV \__declarecategory:nN {#2} \l__category_key_tl
}
\cs_new_protected:Npn \__declarecategory:nN #1#2
{
\exp_args:Nc\NewDocumentCommand{#1}{ }
{
%Do some stuff here with #2:
called:~#1,~
% Do some other stuff, using the \l__category_key_tl token list
Value~is:~#2
}
}
答案3
当仅在内部定义的命令中需要处理键值参数时(到目前为止对我来说就是这种情况),似乎最简单的解决方案就是\keys_set:nn
之内新定义的 DocumentCommand,并定期在那里应用分组。
本质上,这会推迟对可选参数的评估,直到调用内部定义的宏(但缺点是每次都要再次解析这些宏)。
答案4
我只想用\tl_gset:cn
它来全局定义命令。在编程级别 expl3 代码中嵌套文档级别 NewDocumentCommand 声明不一定是错误的,但总是有点可疑。
根据使用情况,您可以使用 expl3“新”版本,或者像这里一样只使用\t_gset
(或多或少\gdef
)
% \exp_args:NNc\global\NewDocumentCommand{#2}{ }{...}
\tl_gset:cn{#2}{...}
也许
% \exp_args:NNc\global\NewDocumentCommand{#2}{ }{...}
\cs_new_protected:cpn{#2}{...}
如果名称不是新的,则会引发错误。