A volte una parte di stato deve essere condivisa tra diverse word senza doverla passare ogni volta attraverso i loro stack effect. Le variabili dinamiche di Factor, dichiarate con SYMBOL:, danno a ogni word accesso a un valore conservato in un namespace.
SYMBOL: e SYMBOLS:
SYMBOL: name dichiara una variabile. SYMBOLS: a b c ; ne dichiara diverse in una sola volta:
USING: namespaces ;
SYMBOLS: counter total ;
set-global e get-global (in namespaces) operano sul namespace globale:
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
Per lo stato a livello di modulo, le varianti -global sono quelle che ti servono.
with-variable
Una variabile dinamica supporta anche una seconda variante, quella con scope. set / get / change (senza il suffisso -global) leggono e scrivono nello scope dinamico corrente, e with-variable (in namespaces) crea uno scope nuovo per la durata di una quotation:
with-variable ( value variable quot -- )
set ( value variable -- )
get ( variable -- value )
change ( variable quot: ( old -- new ) -- )
La variabile è legata a value mentre quot viene eseguita; quando questa termina, il legame scompare. Così chi chiama dall'esterno può «iniettare» un valore che il codice interno può leggere senza doverlo passare attraverso ogni stack signature.
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)
In questo esercizio useremo la variante globale dall'inizio alla fine, ma riconoscere la famiglia parallela set/get/change è utile quando leggi il codice di altre persone.
Dato che le variabili dinamiche contengono un riferimento, puoi conservare una collezione mutabile dentro una di esse e aggiornarla sul posto:
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{ … } è un vettore letterale. Fai sempre clone di un letterale V{ } prima di memorizzarlo: i letterali sono condivisi tra le chiamate.
find: la prima corrispondenzafind (in sequences) restituisce il primo elemento per cui un predicato risulta vero, insieme al suo indice:
find ( seq quot -- index elt/f )
Se non c'è nessuna corrispondenza, entrambi i valori sono f. Scartare l'indice con nip e usare unless* è la combinazione standard per il «valore predefinito se manca qualcosa».
reject: scartare le corrispondenzereject (sempre in sequences) è l'opposto di filter: restituisce gli elementi che non superano il predicato.
La tua associazione di quartiere ti chiede di gestire le registrazioni degli appezzamenti del giardino. Lo stato è contenuto in due variabili dinamiche:
registrations: un array di tuple plot attualmente assegnate a una persona.next-id: l'intero da usare per la prossima registrazione.La tupla plot ha due campi:
| campo | tipo |
|---|---|
id |
intero |
registered-to |
stringa |
Definisci open-garden per inizializzare le variabili dinamiche: un array vuoto per registrations e 1 per next-id. Poi definisci list-registrations per restituire l'array attuale degli appezzamenti.
open-garden
list-registrations .
! => V{ }
Definisci register per prendere un nome dallo stack, costruire un nuovo appezzamento con il prossimo id disponibile, aggiungerlo in coda all'array registrations, incrementare next-id di uno e restituire il nuovo appezzamento.
open-garden
"Emma Balan" register .
! => T{ plot { id 1 } { registered-to "Emma Balan" } }
list-registrations .
! => V{ T{ plot { id 1 } { registered-to "Emma Balan" } } }
Gli id degli appezzamenti devono essere univoci e sempre crescenti, anche dopo un rilascio: next-id non deve mai riutilizzare un valore.
Definisci release per prendere un id e rimuovere da registrations la voce corrispondente. Rilasciare un id sconosciuto non ha alcun effetto.
open-garden
"Emma" register drop
1 release
list-registrations .
! => V{ }
Definisci get-registration per prendere un id e restituire l'appezzamento corrispondente, oppure il simbolo not-found se nessun appezzamento ha quell'id.
open-garden
"Emma" register drop
1 get-registration .
! => T{ plot { id 1 } { registered-to "Emma" } }
7 get-registration .
! => not-found
Definisci find-by-name per prendere un nome e restituire un array di tutti gli appezzamenti attualmente registrati a quella persona.
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" } } }
Iscriviti a Exercism per imparare e padroneggiare Factor con 47 concetti163 esercizi e il mentoring di persone reali, tutto gratis.