有时,一份状态需要在多个单词之间共享,而不必每次都让它在各自的栈效应中层层传递。Factor 的动态变量(用SYMBOL:声明)让每个单词都能访问存放在命名空间里的值。
SYMBOL:和SYMBOLS:
SYMBOL: name声明一个变量。SYMBOLS: a b c ;可以一次声明多个:
USING: namespaces ;
SYMBOLS: counter total ;
set-global和get-global(位于namespaces)操作的是全局命名空间:
set-global ( value variable -- )
get-global ( variable -- value )
change-global ( variable quot: ( old -- new ) -- )
0 counter set-global
counter get-global . ! => 0
counter [ 10 + ] change-global
counter get-global . ! => 10
对于模块级状态,你要的正是带 -global 后缀的那一套写法。
with-variable做作用域绑定动态变量还支持第二种作用域内的用法。set/get/change(不带 -global 后缀)读写的是当前的动态作用域,而with-variable(位于namespaces)会在一段引用执行期间创建一个全新的作用域:
with-variable ( value variable quot -- )
set ( value variable -- )
get ( variable -- value )
change ( variable quot: ( old -- new ) -- )
在quot执行期间,变量会绑定到value;一旦quot返回,这个绑定就消失了。这样,外层的调用方就能“注入”一个值,让内层代码直接读取,而不必把它塞进每一个栈签名里。
SYMBOL: greeting
"Hello" greeting [
greeting get . ! => "Hello"
"Howdy" greeting set
greeting get . ! => "Howdy"
greeting [ "!" append ] change
greeting get . ! => "Howdy!"
] with-variable
greeting get-global . ! => f (the scoped binding is gone)
本练习自始至终都使用全局那一种,不过在你阅读别人的代码时,认出与之对应的set/get/change系列会很有用。
因为动态变量保存的是引用,所以你可以把可变的集合放进一个动态变量里,并就地更新它:
SYMBOL: history
V{ } clone history set-global
"first event" history get-global push
"second event" history get-global push
history get-global .
! => V{ "first event" "second event" }
V{ … }是字面量向量。保存V{ }字面量之前一定要先clone:字面量在多次调用之间是共享的。
find:第一个匹配项find(位于sequences)返回第一个让谓词为真的元素,以及它的下标:
find ( seq quot -- index elt/f )
如果没有任何元素匹配,两个值都是f。用nip丢掉下标,再用unless*,这就是标准的“缺失时取默认值”组合。
reject:丢掉匹配项reject(同样位于sequences)与filter相反:它返回那些未通过谓词的元素。
你的社区协会请你管理花园的地块登记。状态保存在两个动态变量中:
registrations,一个由 plot元组构成的向量,表示当前分配给某个人的地块。next-id,下一次登记要使用的整数。plot元组有两个槽位:
| 槽位 | 类型 |
|---|---|
id |
整数 |
registered-to |
字符串 |
定义 open-garden 来初始化这两个动态变量:把 registrations设为一个空向量,把 next-id设为 1。然后定义 list-registrations,让它返回当前的地块向量。
open-garden
list-registrations .
! => V{ }
定义 register:从栈上取出一个姓名,用下一个可用的 id 构造一个新的 plot,把它追加到 registrations向量中,把 next-id加一,然后返回这个新地块。
open-garden
"Emma Balan" register .
! => T{ plot { id 1 } { registered-to "Emma Balan" } }
list-registrations .
! => V{ T{ plot { id 1 } { registered-to "Emma Balan" } } }
地块 id 必须唯一,而且即使在释放之后也保持递增,next-id绝不能重复使用同一个值。
定义 release:接收一个 id,并从 registrations中删除匹配的条目。释放未知的 id 是无操作。
open-garden
"Emma" register drop
1 release
list-registrations .
! => V{ }
定义 get-registration:接收一个 id,返回匹配的地块;如果没有地块拥有这个 id,就返回符号 not-found。
open-garden
"Emma" register drop
1 get-registration .
! => T{ plot { id 1 } { registered-to "Emma" } }
7 get-registration .
! => not-found
定义 find-by-name:接收一个姓名,返回当前登记在该人名下的所有地块组成的向量。
open-garden
"Emma" register drop
"Bob" register drop
"Emma" register drop
"Emma" find-by-name .
! => V{ T{ plot { id 1 } { registered-to "Emma" } }
T{ plot { id 3 } { registered-to "Emma" } } }