學習軌道
/
Factor
Factor
/
練習
/
花園聚會
花園聚會

花園聚會

學習練習

簡介

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

1. 開啟花園並列出登記資料

定義 open-garden 來初始化動態變數:registrations 用空向量,next-id 用 1。接著定義 list-registrations,回傳目前的圃地向量。

open-garden
list-registrations .
! => V{ }

2. 登記一塊圃地

定義 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 絕不應該重複使用同一個值。

3. 釋放圃地

定義 release:接收一個 id,並從 registrations 移除相符的項目。釋放未知的 id 不會有任何作用。

open-garden
"Emma" register drop
1 release
list-registrations .
! => V{ }

4. 取得已登記的圃地

定義 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

5. 依名字尋找圃地

定義 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" } } }
透過 GitHub 編輯 連結會在新視窗或分頁中開啟
Factor Exercism

準備好開始 花園聚會 了嗎?

註冊 Exercism,透過 47 個概念163 個練習 和真人引導來學習並精通 Factor,全部免費。