有時需要讓某個狀態在多個詞之間共用,而不必每次都在它們的堆疊效應中層層傳遞。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" } } }