在 \begingroup ... \endgroup 内定义全局函数

在 \begingroup ... \endgroup 内定义全局函数

我有一个命令,它接受可选参数作为键值对,由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

这里有两个问题:

  1. \NewDocumentCommand不是全局声明;
  2. 您想要使用为替换文本中的键设置的值,而不是令牌列表名称。

第一个问题可以通过首先将键设置为“标准状态”来解决,从而避免对组的需求。

第二个问题通过扩展定义“控制扩展”来解决。这里我使用了组策略,并且\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}{...}

如果名称不是新的,则会引发错误。

相关内容