在语法中实现某些接口时,什么决定了我应该使用属性列表还是键值列表expl3
?
LaTeX3 文档说
属性列表用于存储代码中使用的基于键的信息。这与键值列表不同,后者是 keys 模块解析的一种输入形式。
(
interface3.pdf
,第 129 页)
这表明,如果我提供文档级界面,我应该使用键值列表而不是属性列表,并且后者应该保留用于更多的内部数据处理。
然而,属性列表经常出现在看似表面上涉及提供用于处理用户输入的文档级界面。例如,egreg 昨天发布了此类代码。
这让我认为我刚刚误解了上面引用的 L3 文档中的区别。
如何理解这种区别?也就是说,在特定情况下,如何决定使用属性列表还是键值列表?
编辑
所以我所拥有的大致是以下稍微复杂一些的版本
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\prop_new:N \l_test_res_prop
\NewDocumentCommand \res { m }
{
\group_begin:
\seq_set_split:Nnn \l_tmpa_seq { , } { #1 }
\seq_map_inline:Nn \l_tmpa_seq
{
\prop_item:Nn \l_test_res_prop { ##1 }
}
\group_end:
}
\cs_new_protected:Nn \test_res_set:nn
{
\prop_if_exist:NF \l_test_res_prop
{
\prop_new:N \l_test_res_prop
}
\prop_put:Nnn \l_test_res_prop { #1 } { #2 }
}
\cs_generate_variant:Nn \test_res_set:nn { VV }
\keys_define:nn { test }
{
res / unknown .code:n = {
\test_res_set:VV \l_keys_key_tl \l_keys_value_tl
},
}
\NewDocumentCommand\testset { +m }{
\keys_set:nn { test } { #1 }
}
\ExplSyntaxOff
\testset{
res/something={\begin{center}\Huge\bfseries\itshape Something \dots\end{center}},
res/this way comes={\begin{center}\Huge\bfseries\itshape \includegraphics[width=.5\linewidth]{cauldron}\par this way comes \dots\end{center}},
res/wicked={\begin{center}\Huge\bfseries\itshape wicked \dots\end{center}},
}
\usepackage{graphicx}
\begin{document}
\res{something}
\res{wicked,this way comes}
\end{document}
这确实是同时使用了键值列表和属性列表。但这真的是合法用法吗?别误会我的意思:它工作得很好,谢谢。但它似乎依赖于可疑的花招……
答案1
总结
这两个概念实际上并没有相互联系,除了它们都基于函数(在数学意义上):一个将值与域的元素相关联。对于键值接口,域是预先确定的(但有unknown
可以扩展它的键),而对于属性列表则不是。但是,使用键值接口来填充属性列表并不是一个奇怪的想法。
扩展答案
使用键值接口并没有错储存属性列表中的数据,事实上您所指的答案就是这样做的。
问题是用用户以键值形式指定的一些数据填充模板。这些数据的存储方式取决于宏要用它们做什么。
如果要按顺序使用数据,则最好使用序列,请参阅https://tex.stackexchange.com/a/352436/4427其中,键值格式中已有的数据被转换为略有不同的格式,适合 BibTeX。
如果数据是按键使用的,那么属性列表就是自然而然要使用的数据类型,而不是设置几个标记列表变量;当然这也是个人喜好的问题和效率。标记列表比属性列表执行得更快。
两者之间没有太大区别
\keys_define:nn { cfr/foo }
{
a .tl_set:N = \l_cfr_foo_a_tl,
b .tl_set:N = \l_cfr_foo_b_tl,
c .tl_set:N = \l_cfr_foo_c_tl,
}
以及一个假设
\keys_define:nn { cfr/foo }
{
a .prop_put:Nn = \l_cfr_foo_prop { a },
b .prop_put:Nn = \l_cfr_foo_prop { b },
c .prop_put:Nn = \l_cfr_foo_prop { c },
}
目前还不可用,但将来可能会可用。可以通过以下方式实现
\prop_new:N \l_cfr_foo_prop
\keys_define:nn { cfr/foo }
{
a .code:n = \prop_put:Nnn \l_cfr_foo_prop { a } { #1 },
b .code:n = \prop_put:Nnn \l_cfr_foo_prop { b } { #1 },
c .code:n = \prop_put:Nnn \l_cfr_foo_prop { c } { #1 },
}
除了效率之外,这两种方法的主要区别在于,第二种方法在使用数据时可能会允许代码稍微更简洁一些。
答案2
我刚刚\prop_set_from_keyval:Nn
在 interface3.pdf 中看到,所以这可能就是您想要使用的。
关于许多人回答的“属性列表与键值列表”的问题,我认为事实是“键值列表”并不存在,它们是一种输入形式,但这样做似乎\setup{foo1=bar,foo2=baz}
比...更方便\setup{foo1}{bar}\setup{foo2}{baz}
。
此命令似乎是 expl3 对此问题的回答:
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\prop_new:N \l_test_res_prop
\NewDocumentCommand \res { m }
{
\clist_map_inline:nn { #1 }
{
\prop_item:Nn \l_test_res_prop { ##1 }
}
}
\NewDocumentCommand \testset { +m }
{
\prop_set_from_keyval:Nn \l_test_res_prop { #1 }
}
\ExplSyntaxOff
\testset{
something={\begin{center}\Huge\bfseries\itshape Something \dots\end{center}},
this way comes={\begin{center}\Huge\bfseries\itshape \includegraphics[width=.5\linewidth]{cauldron}\par this way comes \dots\end{center}},
wicked={\begin{center}\Huge\bfseries\itshape wicked \dots\end{center}}
}
\usepackage{graphicx}
\begin{document}
\res{something}
\res{wicked, this way comes}
\end{document}
如果您不想因为此功能的实验性而依赖它,您可以定义自己的功能,然后在其移至稳定状态时进行更新:
\cs_new_protected:Npn \cfr_prop_put_from_keyval:Nn #1 #2
{
\cs_set:Npn \__cfr_prop_put:nn ##1 ##2 { \prop_put:Nnn #1 { ##1 } { ##2 } }
\keyval_parse:NNn \use_none:n \__cfr_prop_put:nn { #2 }
}