l3keys 中变量设置操作后的代码挂钩

l3keys 中变量设置操作后的代码挂钩

l3keys这是我第一次尝试,遇到了以下问题。我有一些布尔键(真/假)。对于它们来说,它看起来很好用.bool_set:N,这样我就可以自动验证传递给键的值,并且还可以将键值很好地存储在布尔变量中以供以后检查。

同时,我想立即执行一些代码变量已设置。但是,看起来.code:nbool_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}

enter image description here

稍微不同的实现也允许.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}

enter image description here

\end{advertisement}

相关内容