这或多或少是直接跟进这个问题。
问题在于将全局变量的当前值放入 LaTeX3 prop 列表中。诀窍是使用变V
体来获取价值而不是记录变量名对应的token列表。
现在问题是相同的,只不过我需要的是变量的值,而不是计算的输出。我尝试在以下 MWE 中的各个地方使用V
s 和x
s,但我无法得到正确的东西。
\documentclass{article}
\usepackage{expl3,xparse}
\ExplSyntaxOn
\int_new:N \g_seamus_foo_int
\int_new:N \l_seamus_tmpa_int
\cs_new:Npn \seamus_set_int #1 {
\int_set:Nn \g_seamus_foo_int {#1}
}
\cs_new:Npn \seamus_modify_int #1 {
\int_set:Nn \l_seamus_tmpa_int {
\int_mod:nn {\g_seamus_foo_int + #1}{60}
}
\int_gset_eq:NN \g_seamus_foo_int \l_seamus_tmpa_int
}
\cs_new:Npn \seamus_return_int #1 {
\int_compare:nNnTF {\g_seamus_foo_int} > {#1} {Big (\int_eval:n{\g_seamus_foo_int})} {Small (\int_eval:n{\g_seamus_foo_int})}
}
\prop_new:N \g_seamus_prop_list
\cs_new:Npn \seamus_set_value #1#2 {
\prop_gput:NnV \g_seamus_prop_list {#1}{#2}
}
\cs_new:Npn \seamus_get_value #1 {
\prop_get:Nn \g_seamus_prop_list {#1}
}
\NewDocumentCommand\Marker{m}{
\seamus_set_value{#1}{
\seamus_return_int{30}
}
}
\NewDocumentCommand\ReturnMark{m}{
#1: \seamus_get_value{#1}
}
\NewDocumentCommand\SetNumber {m} {
\seamus_set_int{#1}
}
\NewDocumentCommand\ChangeNumber{m}{
\seamus_modify_int{#1}
}
\NewDocumentCommand\PrintNumber{}{
\seamus_return_int{30}
}
\ExplSyntaxOff
\begin{document}
Set at 12: \SetNumber{12}\PrintNumber\\
Add 7: \ChangeNumber{7}\PrintNumber\ (Marker foo) \Marker{foo}\\
Add 12: \ChangeNumber{12}\PrintNumber (Marker bar) \Marker{bar}\\
Add 30: \ChangeNumber{30}\PrintNumber\\
\ReturnMark{foo}\\
\ReturnMark{bar}
\end{document}
这是我能做的最简单的例子。实际用例更加复杂。主要思想是命令应该将prop 列表中输出\Marker{foo}
的当前值保存为键。因此,所需的输出是bar` 应该是“bar: Big (31)”。\seamus_return_int
\g_seamus_prop_list
foo
\ReturnMark{foo} should give "foo: Small (19)" and
但是,\ReturnMark
似乎使用调用时的值,而不是被调用\g_seamus_foo_int
时保存的值。\Marker
我确信有一些我不理解的扩展……
答案1
在我看来,x
当你将值存储在属性列表中时,你想对其进行类型扩展。添加正确的参数规范、需要它的函数的受保护状态、删除一些不必要的代码并修复全局状态会导致类似
\documentclass{article}
\usepackage{expl3,xparse}
\ExplSyntaxOn
\int_new:N \g_seamus_foo_int
\prop_new:N \g_seamus_prop
\cs_new:Npn \seamus_gset_int:n #1
{
\int_gset:Nn \g_seamus_foo_int {#1}
}
\cs_new:Npn \seamus_gmodify_int:n #1
{
\int_gset:Nn \g_seamus_foo_int
{
\int_mod:nn { \g_seamus_foo_int + #1 } { 60 }
}
}
\cs_new:Npn \seamus_return_int:n #1
{
\int_compare:nNnTF \g_seamus_foo_int > {#1}
{ Big~( \int_use:N \g_seamus_foo_int ) }
{ Small~( \int_use:N \g_seamus_foo_int ) }
}
\cs_new_protected:Npn \seamus_set_value:nn #1#2
{ \prop_gput:Nnn \g_seamus_prop {#1} {#2} }
\cs_generate_variant:Nn \seamus_set_value:nn { nx }
\cs_new:Npn \seamus_get_value:n #1
{ \prop_get:Nn \g_seamus_prop {#1} }
\NewDocumentCommand \Marker { m }
{
\seamus_set_value:nx {#1}
{ \seamus_return_int:n { 30 } }
}
\NewDocumentCommand \ReturnMark { m }
{ #1: \seamus_get_value:n {#1} }
\NewDocumentCommand \SetNumber { m }
{ \seamus_gset_int:n {#1} }
\NewDocumentCommand\ChangeNumber { m }
{ \seamus_gmodify_int:n {#1} }
\NewDocumentCommand\PrintNumber { }
{ \seamus_return_int:n { 30 } }
\ExplSyntaxOff
\begin{document}
Set at 12: \SetNumber{12}\PrintNumber\\
Add 7: \ChangeNumber{7}\PrintNumber\ (Marker foo) \Marker{foo}\\
Add 12: \ChangeNumber{12}\PrintNumber (Marker bar) \Marker{bar}\\
Add 30: \ChangeNumber{30}\PrintNumber\\
\ReturnMark{foo}\\
\ReturnMark{bar}
\ExplSyntaxOn
\prop_show:N \g_seamus_prop
\ExplSyntaxOff
\end{document}
一般来说,如果你想要强制一个条目具有某种“结果”,你很可能正在寻找x
-type 扩展。为此,你当然需要确保底层代码是可扩展的。
函数\cs_generate_variant:Nn
通常用于expl3
编程以控制扩展。对于函数\foo:n
,参数按原样使用,例如在存储的情况下,这意味着在
\int_set:Nn \l_tmpa_int { 10 }
\tl_set:Nn \l_tmpa_tl { \int_use:N \l_tmpa_int }
标记列表包含文字\int_use:N \l_tmpa_int
而不是计数器的值(10
)。
通过创建变体,我们可以定义参数在传递给基函数之前如何扩展。因此
\cs_generate_variant:Nn \tl_set:Nn { Nx }
允许
\tl_set:Nx \l_tmpa_tl { \int_use:N \l_tmpa_int }
现在,在应用x
基函数之前,参数首先经过-type 扩展。(注意:将保持现有变体不变,因此对于手动调整的变体(例如!)而言是“安全的”。)\tl_set:Nn
\cs_generate_variant:Nn
\tl_set:nx
作为编程的一般原则expl3
,除非执行低级工作(例如使用分隔参数),否则通常应该使用变体而不是“硬编码”方法扩展参数。
中的函数(宏)的可扩展性expl3
通常应定义明确。完全可扩展的函数应使用\cs_new:Npn
或类似方法进行定义,而那些不可扩展的函数应使用 创建\cs_new_protected:Npn
。expl3
文档指出了哪些函数是可扩展的:如果您创建一个新函数,并且它仅有的使用可扩展功能那么它本身也是可扩展的,否则就不可扩展。
采用这种方法的原因是,这意味着在如下结构中
\tl_set:Nx \l_tmpa_tl { \foo:n { arg } }
任何一个
\foo:n
将可扩展并正常工作以给出“结果”\foo:n
将受到保护,因此不会因x
-type 扩展而改变
使用 LaTeX2e 的经验表明,宏存在“意外”部分扩展的问题。(请注意,ConTeXt 采取了类似的全有或全无方法。)