访问同名的选项和参数

访问同名的选项和参数

我试图理解 latex3 中变量的范围,并构建一个函数的示例,该函数采用命令的选项和参数,并且两者(选项和参数)具有相同的名称。

注意:我在示例中明确使用了相同的名称来找出并了解变量的范围。

更新我知道我对两个不同的东西使用了相同的名称。这是故意的。这样做的原因是为了能够测试并了解哪些部分是封装的,哪些不是。据我所知,和 中有某种封装group_begingroup_end这种封装似乎也传播到组内调用的函数。但在我看来, 和 中keys_define没有封装。

在调用中,\mycommand[name=optionname]{name=argname}我们可以对两个不同的东西使用相同的名称。这在两个不同的东西中可能吗keys_define?如果不行,技术原因是什么?

从其他编程语言的角度来看,我正在寻找类似mymodule_myoptions::l_mymodule_name_tl和的东西mymodule_myargs::l_mymodule_name_tl

在我的例子中,我想调用\mycommand[name=optionname]{name=argname}keys_set的各自的选项和参数,并使用选项(作为#1)和参数(作为#2)keys_define调用一个函数,并且这个函数应该输出两者。\__mymodule_ShowOptionsAndArgs:nn #1 #2namenameoption=optionname; arg=argname

下面的示例输出以下内容(另请参阅代码中的注释):

option=initoptionname; arg=initargname
option=optionname;     arg=initargname
option=initoptionname; arg=argname
option=initoptionname; arg=initargname

我应该如何改变我的例子来得到

option=optionname; arg=argname

我该怎么做?最佳实践是什么?

\documentclass{article}

\ExplSyntaxOn

\keys_define:nn { mymodule / myoptions }
  { 
    name .tl_set:N = \l_mymodule_name_tl,
  }

\keys_define:nn { mymodule / myargs }
  { 
    name .tl_set:N = \l_mymodule_name_tl,
  }

\cs_new_protected:Npn \__mymodule_ShowOptionsAndArgs:nn #1 #2
  {
    \par option = #1 ;~ arg = #2
  }
\cs_new_protected:Npn \__mymodule_myfunction:nn #1#2
  {
    \tl_new:N \l_optionname_tl
    \tl_new:N \l_argname_tl
    \tl_set:Nn \l_optionname_tl { initoptionname }
    \tl_set:Nn \l_argname_tl { initargname }

    \__mymodule_ShowOptionsAndArgs:nn { \l_optionname_tl } { \l_argname_tl }
    % -> option=initoptionname; arg=initargname
    \group_begin:
      \keys_set:nn { mymodule / myoptions } { #1 }
      \tl_set:Nn \l_optionname_tl { \l_mymodule_name_tl }
      \__mymodule_ShowOptionsAndArgs:nn { \l_optionname_tl } { \l_argname_tl }
      % -> option=optionname; arg=initargname
    \group_end:

    \group_begin:
      \keys_set:nn { mymodule / myargs } { #2 }
      \tl_set:Nn \l_argname_tl { \l_mymodule_name_tl }
      \__mymodule_ShowOptionsAndArgs:nn { \l_optionname_tl } { \l_argname_tl }
      % -> option=initoptionname; arg=argname
    \group_end:

    \__mymodule_ShowOptionsAndArgs:nn { \l_optionname_tl } { \l_argname_tl }
    % -> option=initoptionname; arg=initargname
  }
\NewDocumentCommand{\mycommand}{ o m }
  {
    \__mymodule_myfunction:nn { #1 } { #2 }
  }

\ExplSyntaxOff

\begin{document}
\mycommand[name=optionname]{name=argname}
\end{document}

答案1

变量、控制序列、命令、宏……这些都可以在本地或全局进行定义或重新定义。如果更改是本地的,则其影响包含在当前 TeX 组中。

可以使用无数不同的语法变体/命令来开始和结束组,具体取决于上下文。以下列表仅用于说明一些最常见的构造。在每种情况下,“...”代表一个本地组。在此组内进行的本地更改在组结束后无效。

  • \group_begin:... \group_end:expl3
  • \group... \endgroup(文本)
  • {...}其中括号(花括号)不具有包含参数的功能。例如,\section{Section}不会创建组,因为{}只是界定参数。
  • \begin{<environment>}... \end{<environment>}(LaTeX 2e)

如果您希望更改在当前组结束后仍然有效,则必须将其设置为全局更改。如果您希望更改仅影响当前组,则应将其设置为本地更改。

在中expl3,变量应该用作本地变量或全局变量,并且它们的名称应该表明这一点。

  • \l_<module>_<description>_<type>应仅在本地设置,例如\tl_set:Nn用于令牌列表。
  • \g_<module>_<description>_<type>应该仅全局设置,例如\tl_gset:Nn用于令牌列表。

\l_mymodule_name_tl或之类的变量\g_mymodule_name_tl不能同时用于两个不同的标记列表。如果您需要同时捕获两个不同的标记列表,则需要两个变量。

由于您希望每个name键将标记列表变量设置为不同的标记列表,并同时访问这两个不同的标记列表,因此您不能对两个键使用相同的标记列表变量。相反,您需要类似

\keys_define:nn { mymodule / myoptions }
  { 
    name .tl_set:N = \l_mymodule_myoptions_name_tl,
  }

\keys_define:nn { mymodule / myargs }
  { 
    name .tl_set:N = \l_mymodule_myargs_name_tl,
  }

但是如果您希望更改在之后继续存在\group_end:,则需要使用全局变量而不是局部\tl_gset:Nn变量\tl_set:Nn

考虑

\documentclass{article}

\ExplSyntaxOn
\tl_new:N \g_optionname_tl
\tl_new:N \g_argname_tl
\tl_gset:Nn \g_optionname_tl { initoptionname }
\tl_gset:Nn \g_argname_tl { initargname }

\keys_define:nn { mymodule / myoptions }
  { 
    name .tl_set:N = \l_mymodule_myoptions_name_tl,
  }
\keys_define:nn { mymodule / myargs }
  { 
    name .tl_set:N = \l_mymodule_myargs_name_tl,
  }

\cs_new_protected:Npn \__mymodule_ShowOptionsAndArgs:nnnn #1 #2 #3 #4
  {
    \par option = #1 ;~ arg = #2 ; ~ oname = #3 ; ~ aname = #4
  }
  \cs_generate_variant:Nn \__mymodule_ShowOptionsAndArgs:nnnn { VVVV }

\cs_new_protected:Npn \__mymodule_myfunction:nn #1#2
  {
    \__mymodule_ShowOptionsAndArgs:VVVV  \g_optionname_tl  \g_argname_tl \l_mymodule_myoptions_name_tl \l_mymodule_myargs_name_tl
    % -> option=initoptionname; arg=initargname
    \group_begin:
      \keys_set:nn { mymodule / myoptions } { #1 }
      \tl_gset:NV \g_optionname_tl \l_mymodule_myoptions_name_tl 
      \__mymodule_ShowOptionsAndArgs:VVVV  \g_optionname_tl  \g_argname_tl \l_mymodule_myoptions_name_tl \l_mymodule_myargs_name_tl
      % -> option=optionname; arg=initargname
    \group_end:    

    \group_begin:
      \keys_set:nn { mymodule / myargs } { #2 }
      \tl_gset:NV \g_argname_tl  \l_mymodule_aname_tl 
      \__mymodule_ShowOptionsAndArgs:VVVV  \g_optionname_tl  \g_argname_tl \l_mymodule_myoptions_name_tl \l_mymodule_myargs_name_tl
      % -> option=optionname; arg=argname
    \group_end:

    \__mymodule_ShowOptionsAndArgs:VVVV  \g_optionname_tl  \g_argname_tl \l_mymodule_myoptions_name_tl \l_mymodule_myargs_name_tl
    % -> option=optionname; arg=argname
  }
\NewDocumentCommand{\mycommand}{ o m }
  {
    \__mymodule_myfunction:nn { #1 } { #2 }
  }

\ExplSyntaxOff

\begin{document}
\mycommand[name=optionname]{name=argname}
\end{document}

键在本地设置\l_mymodule_myoptions_name_tl\l_mymodule_myargs_name_tl,但我们使用值在全局设置\g_optionname_tl\g_argname_tl。另一个区别是,本地变量最初为空,而全局变量则不是。

整体变化与局部变化的影响

请注意,无论等如何,全局变化都会累积\group_begin:,而局部变化则会在时消失\group_end:

答案2

在努力理解了底层概念之后,感谢这里发表的宝贵评论,我想到了一个可能的解决方案。该方法是使用属性列表实现的,并让其keys_define键以子组名称为“前缀”添加:

\documentclass{article}
\ExplSyntaxOn
\prop_new:N \l_mypropertylist
\keys_define:nn { mymodule / options }
  { name .code:n = \prop_put:Nnn \l_mypropertylist { options / name } {#1}, }
\keys_define:nn { mymodule / args }
  { name .code:n = \prop_put:Nnn \l_mypropertylist { args / name } {#1}, }
\cs_new_protected:Npn \__mymodule_DoSomethingWithBothNameVariables:nn #1 #2
  { \par option = #1 ;~ arg = #2 }
\cs_new_protected:Npn \__mymodule_myfunction:nn #1#2
  {
    \keys_set:nn { mymodule / options } { #1 }
    \keys_set:nn { mymodule / args } { #2 }
    \prop_map_function:NN \l_mypropertylist \msg_show_item:nn
    \__mymodule_DoSomethingWithBothNameVariables:nn 
      { \prop_item:Nn \l_mypropertylist { options / name } }
      { \prop_item:Nn \l_mypropertylist { args / name } }
  }
\NewDocumentCommand{\mycommand}{ o m }
  { \__mymodule_myfunction:nn { #1 } { #2 } }
\ExplSyntaxOff
\begin{document}
\mycommand[name=optionname]{name=argname}
\end{document}

输出为

> {options/name} => {optionname}
> {args/name} => {argname}
option=optionname; arg=argname

更新这种方法的优点/特点之一是可以随时使用 轻松访问/报告模块的“状态”(即选项和参数的当前值)\prop_map_function:NN \l_mypropertylist <function>。这是我在 中错过的一项功能/增强功能l3keys:报告和访问定义的键的当前状态。据我所知,l3keys仅提供\l_keys_path_str\l_keys_key_str和 ,\l_keys_value_tl它们仅存储最后处理的键。

相关内容