Manchmal muss ein Zustand zwischen mehreren Wörtern geteilt werden, ohne ihn jedes Mal durch ihre Stack-Effekte zu schleusen. Factors dynamische Variablen, die mit SYMBOL: deklariert werden, geben jedem Wort Zugriff auf einen Wert, der in einem Namensraum gespeichert ist.
SYMBOL: und SYMBOLS:
SYMBOL: name deklariert eine Variable. SYMBOLS: a b c ; deklariert mehrere auf einmal:
USING: namespaces ;
SYMBOLS: counter total ;
set-global und get-global (in namespaces) arbeiten auf dem globalen Namensraum:
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
Für Zustand auf Modulebene sind die -global-Varianten genau das Richtige.
with-variable
Eine dynamische Variable unterstützt außerdem eine zweite, bereichsgebundene Variante. set / get / change (ohne das Suffix -global) lesen und schreiben den aktuellen dynamischen Gültigkeitsbereich, und with-variable (in namespaces) erzeugt einen frischen Gültigkeitsbereich für die Dauer einer Quotation:
with-variable ( value variable quot -- )
set ( value variable -- )
get ( variable -- value )
change ( variable quot: ( old -- new ) -- )
Die Variable ist an value gebunden, während quot läuft; sobald es zurückkehrt, verschwindet die Bindung. So kann ein äußerer Aufrufer einen Wert „injizieren“, den innerer Code lesen kann, ohne ihn durch jede Stack-Signatur zu schleusen.
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)
Diese Übung verwendet durchgehend die globale Variante. Es lohnt sich aber, die parallele Familie set/get/change zu erkennen, wenn du Code von anderen liest.
Weil dynamische Variablen eine Referenz halten, kannst du darin eine veränderliche Sammlung speichern und sie an Ort und Stelle aktualisieren:
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{ … } ist ein Vektor-Literal. clone ein V{ }-Literal immer, bevor du es speicherst: Literale werden zwischen Aufrufen geteilt.
find: der erste Trefferfind (in sequences) gibt das erste Element zurück, für das ein Prädikat wahr ist, zusammen mit seinem Index:
find ( seq quot -- index elt/f )
Wenn nichts übereinstimmt, sind beide Werte f. Den Index mit nip wegzulassen und unless* zu verwenden ist die übliche Kombination für „Standardwert, falls nichts gefunden wird“.
reject: die Treffer verwerfenreject (ebenfalls in sequences) ist das Gegenteil von filter: Es gibt die Elemente zurück, die das Prädikat nicht erfüllen.
Der Verein deines Gemeinschaftsgartens bittet dich, die Beetregistrierungen zu verwalten. Der Zustand steckt in zwei dynamischen Variablen:
registrations: ein Vektor aus plot-Tupeln, die aktuell einer Person zugewiesen sind.next-id: die Ganzzahl, die für die nächste Registrierung verwendet werden soll.Das plot-Tupel hat zwei Felder:
| Feld | Typ |
|---|---|
id |
Ganzzahl |
registered-to |
String |
Definiere open-garden, um die dynamischen Variablen zu initialisieren: einen leeren Vektor für registrations und 1 für next-id. Definiere dann list-registrations, um den aktuellen Vektor der Beete zurückzugeben.
open-garden
list-registrations .
! => V{ }
Definiere register, um einen Namen vom Stack zu nehmen, ein neues plot-Tupel mit der nächsten verfügbaren ID zu erzeugen, es an den Vektor registrations anzuhängen, next-id um eins zu erhöhen und das neue Beet zurückzugeben.
open-garden
"Emma Balan" register .
! => T{ plot { id 1 } { registered-to "Emma Balan" } }
list-registrations .
! => V{ T{ plot { id 1 } { registered-to "Emma Balan" } } }
Beet-IDs müssen eindeutig sein und auch nach einer Freigabe weiter ansteigen. next-id darf niemals einen Wert erneut verwenden.
Definiere release, um eine ID zu nehmen und den passenden Eintrag aus registrations zu entfernen. Wenn du eine unbekannte ID freigibst, ist das ein No-Op.
open-garden
"Emma" register drop
1 release
list-registrations .
! => V{ }
Definiere get-registration, um eine ID zu nehmen und das passende Beet zurückzugeben, oder das Symbol not-found, wenn kein Beet diese ID hat.
open-garden
"Emma" register drop
1 get-registration .
! => T{ plot { id 1 } { registered-to "Emma" } }
7 get-registration .
! => not-found
Definiere find-by-name, um einen Namen zu nehmen und einen Vektor aller Beete zurückzugeben, die aktuell auf diese Person registriert sind.
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" } } }
Melde dich bei Exercism an, um Factor mit 47 Konzepte163 Übungen und echtem menschlichen Mentoring zu lernen und zu meistern, alles kostenlos.