トラック
/
Factor
Factor
/
演習
/
庭の集い
庭の集い

庭の集い

学習演習

はじめに

状態をいくつかのワードの間で共有したいけれど、そのたびにスタック効果を通して受け渡すのは避けたい、ということがあります。Factorの動的変数は、SYMBOL:で宣言し、名前空間に保存された値に各ワードからアクセスできるようにします。

SYMBOL:とSYMBOLS:

SYMBOL: nameは変数を1つ宣言します。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によるスコープ付きの束縛

動的変数には、もう1つスコープ付きの使い方があります。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の反対で、述語を満たさない要素を返します。

説明

地域の組合から、庭の区画の登録を管理するよう頼まれています。状態は2つの動的変数に保持されます。

  • registrations — 現在ある人に割り当てられているplotタプルのベクターです。
  • next-id — 次の登録に使う整数です。

plotタプルには2つのスロットがあります。

スロット 型
id 整数
registered-to 文字列

1. 庭を開いて登録者を一覧表示する

open-gardenを定義して、動的変数を初期化します。registrationsには空のベクター、next-idには1を設定します。次にlist-registrationsを定義して、現在の区画のベクターを返します。

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

2. 区画を登録する

registerを定義します。スタックから名前を取り出し、次に使えるidで新しいplotを作り、registrationsベクターに追加し、next-idを1つ増やして、新しいplotを返します。

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を受け取り、一致するplotを返します。その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を学んでマスターできます。すべて無料です。