l3keys
这是我第一次尝试,遇到了以下问题。我有一些布尔键(真/假)。对于它们来说,它看起来很好用.bool_set:N
,这样我就可以自动验证传递给键的值,并且还可以将键值很好地存储在布尔变量中以供以后检查。
同时,我想立即执行一些代码后变量已设置。但是,看起来.code:n
和bool_set:N
不能在同一个键上一起使用,因为后面的那个替代了第一个的操作。实际上,似乎不可能运行由变量设置操作触发的钩子。
显然,有几种可能的解决方法:
每次执行后都运行一些代码
key_set:nn
。
但是,这意味着这样的代码需要检查每个可能设置的变量,看它是否已更改并采取相应的措施。如果您有 30 个变量,这意味着要检查所有变量,而可能只有其中一个变量被更改。只要变量之间不相互影响,只为已设置的变量调用一个钩子似乎更合适。而不是使用
.code:n
变量设置操作。
但是,这意味着重新进行值的解析和验证。
因为所有的解决方法似乎都有其自身的低效率,所以我想知道我是否遗漏了什么......任何帮助都值得感激!
答案1
这不是一个真正的解决方案,更像是一个可行的解决方法:
您可以创建一个.meta:n
密钥(即记录的前置密钥)和两个附加密钥(一个用于.bool_set:N
,一个用于.code:n
):
\documentclass[]{article}
\usepackage{xparse}
\ExplSyntaxOn
\keys_define:nn { Callegar }
{
my-bool .meta:n = { my-bool-bool = { #1 }, my-bool-code = { #1 } },
my-bool-bool .bool_set:N = \l_Callegar_bool,
my-bool-code .code:n =
{ Execute~Some~Code~for~\bool_if:NTF \l_Callegar_bool { true } { false } }
}
\NewDocumentCommand \setmykeys { m }
{
\keys_set:nn { Callegar } { #1 }
}
\ExplSyntaxOff
\begin{document}
\setmykeys{my-bool=true}
\end{document}
答案2
以下内容重新定义了模块的内部函数l3keys
,因此将来可能会造成破坏。请谨慎使用。
在仔细研究了l3keys
代码之后,我认为可以给键添加一个钩子。下面通过向 中添加钩子机制来实现这一点\__keys_cmd_set:nn
。之后,可以使用以下命令定义一个钩子:
\keys_define:nn { <module> } { <key> .hook:n = { <hook code> } }
我没有彻底测试过,但就快速浏览来看l3keys
,钩子机制应该适用于任何键类型。请注意,它不会自行创建任何键,因此您必须在已定义的键上使用它。
\documentclass[]{article}
\usepackage{xparse}
\ExplSyntaxOn
% redefine a part of the internals of l3keys
\cs_set_protected:Npn \__keys_cmd_set:nn #1#2
{
\cs_set_protected:cpn { \c__keys_code_root_tl #1 } ##1
{ #2 \use:c { \c__keys_code_root_tl #1 _hook } }
}
% add the .hook:n type
\cs_new_protected:cpn { \c__keys_props_root_tl .hook:n } #1
{
\tl_gset:cn { \c__keys_code_root_tl \l_keys_path_tl _hook } { #1 }
}
\keys_define:nn { Callegar }
{
my-fool .bool_set:N = \l_Callegar_bool,
my-fool .hook:n = { Execute~Some~Code~Regardless },
my-fool / true .hook:n =
{ Execute~Some~Hook~Code~for~true },
my-fool / false .hook:n =
{ Execute~Some~Hook~Code~for~false },
}
\NewDocumentCommand \setmykeys { m }
{
\keys_set:nn { Callegar } { #1 }
}
\ExplSyntaxOff
\begin{document}
\setmykeys{my-fool=true}
\setmykeys{my-fool=false}
\end{document}
稍微不同的实现也允许.hook:n
访问值:
\cs_set_protected:Npn \__keys_cmd_set:nn #1#2
{
\cs_set_protected:cpn { \c__keys_code_root_tl #1 } ##1
{
#2
\cs_if_exist_use:cT { \c__keys_code_root_tl #1 _hook } { { ##1 } }
}
}
% add the .hook:n type
\cs_new_protected:cpn { \c__keys_props_root_tl .hook:n } #1
{
\cs_set:cpn { \c__keys_code_root_tl \l_keys_path_tl _hook } ##1 { #1 }
}
\keys_define:nn { Callegar }
{
my-fool .bool_set:N = \l_Callegar_bool,
my-fool .hook:n = { Execute~Some~Code~Regardless~'#1' },
my-fool / true .hook:n =
{ Execute~Some~Hook~Code~for~true },
my-fool / false .hook:n =
{ Execute~Some~Hook~Code~for~false },
}
答案3
\begin{advertisement}[I'm the author of expkv]
您可以使用expkv-def
它,它具有also
将一个处理程序的代码添加到现有密钥的前缀。
\documentclass[]{article}
\usepackage{expkv-def}
\makeatletter
\ekvdefinekeys{Callegar}
{
boolTF my-bool = \Callegar@bool
,also code my-bool = Execute some code for \Callegar@bool{true}{false}
% the automatically set up default of a boolTF wouldn't call the also-code
% part because it gets optimised for speed, this here fixes that and
% reapplies the same optimisation.
,protected fdefault my-bool = true
% alternatively we could use the following:
%,also noval my-bool = Execute some code for true
}
\ekvsetdef\setmykeys{Callegar}
\makeatother
\begin{document}
\setmykeys{my-bool=true}
\setmykeys{my-bool}
\end{document}
\end{advertisement}