预编译 l3keys 中的扩展

预编译 l3keys 中的扩展

我在预编译 l3keys 的扩展方面遇到了一些问题。我想定义一个命令,如果没有给出选项\NewThm{<name>}[<options>],则将显示的定理名称设置为给定的标题版本,否则设置为的值。这是我的尝试:<name>namename

\documentclass{article}
\usepackage{amsthm}

\ExplSyntaxOn

\keys_define:nn { thmkeys/thm }
  {
    name .tl_set:N = \l_thmkeys_thm_name_tl,
  }

\tl_new:N \l_thmkeys_thm_defaultkeys_tl
\keys_precompile:nnN { thmkeys/thm }
  {
    name = \text_titlecase:n { \l_thmkeys_thm_envname_tl },
  }
  \l_thmkeys_thm_defaultkeys_tl

\NewDocumentCommand { \NewThm } { m O{} }
  { 
  % Store envname
    \tl_set:Nn \l_thmkeys_thm_envname_tl { #1 }
  % Set default keys
    \tl_use:N \l_thmkeys_thm_defaultkeys_tl
  % Set env-specific keys
    \keys_set:nn { thmkeys/thm } { #2 }
  % Call \newtheorem
    \thmkeys_thm_new:nV { #1 } \l_thmkeys_thm_name_tl
  }

\cs_new_eq:NN \thmkeys_thm_new:nn \newtheorem
\cs_generate_variant:Nn \thmkeys_thm_new:nn { nV }

\ExplSyntaxOff

\NewThm{theorem}
\NewThm{prop}[name=\textit{Proposition}]
\NewThm{lemma}
    
\begin{document}

\begin{theorem}
text
\end{theorem}

\begin{prop}
text
\end{prop}

\begin{lemma}
text
\end{lemma}

\end{document}

韋姆斯

我理解它为什么会失败:\l_thmkeys_thm_envname_tl在调用期间没有扩展\NewThm,因此每个没有给定name键的定理都会具有相同的显示名称。

我尝试过一些方法来修复它:

  1. 告诉\newtheorem使用e-type 扩展,如\cs_generate_variant:Nn \thmkeys_thm_new:nn { ne }。这修复了显示的问题,但如果键name包含纯文本以外的内容(如示例),则会失败name=\textit{proposition}
  2. 手动扩展 的参数\text_titlecase:nn。在 之前添加\exp_args:Ne(或Vf)\text_titlecase:n { \l_thmkeys_thm_envname_tl }无效。

\l_thmkeys_thm_envname_tl在定义过程中我还能如何扩展\NewThm以便它在包含命令时不会中断?

答案1

您可以使用\text_expand:n一种适用于命令的扩展(但可能存在无法正确处理的边缘情况,\text_expand:n这不是最简单的宏)。

但老实说,我认为以不同的方式设置名称宏比通过冻结/预编译的 kv 列表中的变量更容易。

无论如何,首先显示您当前方式的代码,但是\text_expand:n

\documentclass{article}
\usepackage{amsthm}

\ExplSyntaxOn

\keys_define:nn { thmkeys/thm }
  {
    name .tl_set:N = \l_thmkeys_thm_name_tl,
  }

\tl_new:N \l_thmkeys_thm_defaultkeys_tl
\keys_precompile:nnN { thmkeys/thm }
  {
    name = \text_titlecase:n { \l_thmkeys_thm_envname_tl },
  }
  \l_thmkeys_thm_defaultkeys_tl

\NewDocumentCommand { \NewThm } { m O{} }
  { 
  % Store envname
    \tl_set:Nn \l_thmkeys_thm_envname_tl { #1 }
  % Set default keys
    \tl_use:N \l_thmkeys_thm_defaultkeys_tl
  % Set env-specific keys
    \keys_set:nn { thmkeys/thm } { #2 }
  % Call \newtheorem
    \thmkeys_thm_new:ne { #1 } { \text_expand:n { \l_thmkeys_thm_name_tl } }
  }

\cs_new_eq:NN \thmkeys_thm_new:nn \newtheorem
\cs_generate_variant:Nn \thmkeys_thm_new:nn { ne }

\ExplSyntaxOff

\NewThm{theorem}
\NewThm{prop}[name=\textit{Proposition}]
\NewThm{lemma}
    
\begin{document}

\begin{theorem}
text
\end{theorem}

\begin{prop}
text
\end{prop}

\begin{lemma}
text
\end{lemma}

\end{document}

一种不使用预编译的 kv 列表的变体,而是在name内部设置默认值(至少对于 )\keys_set:nn

\documentclass{article}
\usepackage{amsthm}

\ExplSyntaxOn

\keys_define:nn { thmkeys/thm }
  {
    name .tl_set:N = \l_thmkeys_thm_name_tl,
  }

\NewDocumentCommand { \NewThm } { m O{} }
  { 
  % Store envname
    \tl_set:Nn \l_thmkeys_thm_envname_tl { #1 }
  % Set env-specific keys
    \exp_args:Nne \keys_set:nn { thmkeys/thm }
      { name = { \text_titlecase:n {#1} }, \exp_not:n {#2} }
  % Call \newtheorem
    \thmkeys_thm_new:nV { #1 } { \l_thmkeys_thm_name_tl }
  }

\cs_new_eq:NN \thmkeys_thm_new:nn \newtheorem
\cs_generate_variant:Nn \thmkeys_thm_new:nn { nV }

\ExplSyntaxOff

\NewThm{theorem}
\NewThm{prop}[name=\textit{Proposition}]
\NewThm{lemma}
    
\begin{document}

\begin{theorem}
text
\end{theorem}

\begin{prop}
text
\end{prop}

\begin{lemma}
text
\end{lemma}

\end{document}

另一个版本使用expkv1而不是l3keys.也具有预编译键列表的功能,但预编译expkv内部的工作方式略有不同。此处的键列表不是固定的(好吧,键列表是固定的,但值不是),而是像插入键值处理一样工作。这样,您可以预编译键,但仍可以通过宏参数为各个键赋予不同的值。expkv\ekvcompile\cs_set:Npn

expkv-def(基于 kv-list 的前端,用于定义 中的键expkv)中, 的等价物.tl_set:N名为store。请注意,expkv使用空格分隔的前缀而不是.-分隔的后缀来表示键类型。

缺点是您将非 L3 语法混入您的代码中,因为它exkpv不是用 L3 编程的,并且没有前端。

1免责声明:我是expkv

\documentclass{article}
\usepackage{amsthm}

\usepackage{expkv-def}

\ExplSyntaxOn

\ekvdefinekeys { thmkeys/thm }
  {
    store~ name = \l_thmkeys_thm_name_tl
  }
\ekvcompile \__thmkeys_defaults:n #1 { thmkeys/thm } { name = {#1} }
\cs_generate_variant:Nn \__thmkeys_defaults:n { e }

\NewDocumentCommand { \NewThm } { m O{} }
  { 
  % Store envname
    \tl_set:Nn \l_thmkeys_thm_envname_tl { #1 }
  % Set defaults
    \__thmkeys_defaults:e { \text_titlecase:n { \l_thmkeys_thm_envname_tl } }
  % Set env-specific keys
    \ekvset { thmkeys/thm } { #2 }
  % Call \newtheorem
    \thmkeys_thm_new:nV { #1 } { \l_thmkeys_thm_name_tl }
  }

\cs_new_eq:NN \thmkeys_thm_new:nn \newtheorem
\cs_generate_variant:Nn \thmkeys_thm_new:nn { nV }

\ExplSyntaxOff

\NewThm{theorem}
\NewThm{prop}[name=\textit{Proposition}]
\NewThm{lemma}
    
\begin{document}

\begin{theorem}
text
\end{theorem}

\begin{prop}
text
\end{prop}

\begin{lemma}
text
\end{lemma}

\end{document}

另一个纯 L3 答案可能是,不要将您的name密钥直接设置为您的envname,而是将其设置为\q_no_value,然后检查它是否为\q_no_value,如果是,则使用您的envname

这种变体具有额外的优点,即\text_titlecase:n仅在真正必要时才使用(并且\text_titlecase:n相对较慢)。

\documentclass{article}
\usepackage{amsthm}

\ExplSyntaxOn

\keys_define:nn { thmkeys/thm }
  {
    name .tl_set:N = \l_thmkeys_thm_name_tl,
  }

\tl_new:N \l_thmkeys_thm_defaultkeys_tl
\keys_precompile:nnN { thmkeys/thm }
  {
    name = \q_no_value,
  }
  \l_thmkeys_thm_defaultkeys_tl

\NewDocumentCommand { \NewThm } { m O{} }
  { 
  % Store envname
    \tl_set:Nn \l_thmkeys_thm_envname_tl { #1 }
  % Set default keys
    \tl_use:N \l_thmkeys_thm_defaultkeys_tl
  % Set env-specific keys
    \keys_set:nn { thmkeys/thm } { #2 }
  % Call \newtheorem
    \quark_if_no_value:NTF \l_thmkeys_thm_name_tl
      {
        \thmkeys_thm_new:ne {#1}
          { \text_titlecase:n { \l_thmkeys_thm_envname_tl } }
      }
      { \thmkeys_thm_new:nV {#1} \l_thmkeys_thm_name_tl }
  }

\cs_new_eq:NN \thmkeys_thm_new:nn \newtheorem
\cs_generate_variant:Nn \thmkeys_thm_new:nn { nV, ne }

\ExplSyntaxOff

\NewThm{theorem}
\NewThm{prop}[name=\textit{Proposition}]
\NewThm{lemma}
    
\begin{document}

\begin{theorem}
text
\end{theorem}

\begin{prop}
text
\end{prop}

\begin{lemma}
text
\end{lemma}

\end{document}

相关内容