我在预编译 l3keys 的扩展方面遇到了一些问题。我想定义一个命令,如果没有给出选项\NewThm{<name>}[<options>]
,则将显示的定理名称设置为给定的标题版本,否则设置为的值。这是我的尝试:<name>
name
name
\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
键的定理都会具有相同的显示名称。
我尝试过一些方法来修复它:
- 告诉
\newtheorem
使用e
-type 扩展,如\cs_generate_variant:Nn \thmkeys_thm_new:nn { ne }
。这修复了显示的问题,但如果键name
包含纯文本以外的内容(如示例),则会失败name=\textit{proposition}
。 - 手动扩展 的参数
\text_titlecase:nn
。在 之前添加\exp_args:Ne
(或V
或f
)\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}
另一个版本使用expkv
1而不是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}