Il arrive qu'un morceau d'état doive être partagé entre plusieurs mots sans avoir à le faire passer par leur effet de pile à chaque fois. Les variables dynamiques de Factor, déclarées avec SYMBOL:, donnent à chaque mot accès à une valeur stockée dans un espace de noms.
SYMBOL: et SYMBOLS:
SYMBOL: name déclare une variable. SYMBOLS: a b c ; en déclare plusieurs d'un coup :
USING: namespaces ;
SYMBOLS: counter total ;
set-global et get-global (dans namespaces) agissent sur l'espace de noms global :
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
Pour un état au niveau du module, ce sont les variantes -global qu'il te faut.
with-variable
Une variable dynamique prend aussi en charge une seconde variante, à portée limitée. set / get / change (sans le suffixe -global) lisent et écrivent la portée dynamique courante, et with-variable (dans namespaces) crée une nouvelle portée pendant toute la durée d'une quotation :
with-variable ( value variable quot -- )
set ( value variable -- )
get ( variable -- value )
change ( variable quot: ( old -- new ) -- )
La variable est liée à value pendant l'exécution de quot ; dès que celle-ci se termine, la liaison disparaît. Cela permet à un appelant extérieur d'« injecter » une valeur que le code interne peut lire sans avoir à la faire passer par chaque signature de pile.
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)
Cet exercice n'utilise que la variante globale, mais reconnaître la famille parallèle set/get/change est utile quand tu lis le code des autres.
Comme les variables dynamiques contiennent une référence, tu peux y ranger une collection mutable et la modifier sur place :
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{ … } est un vecteur littéral. Fais toujours un clone sur un littéral V{ } avant de le stocker : les littéraux sont partagés entre les appels.
find : le premier élément correspondantfind (dans sequences) renvoie le premier élément pour lequel un prédicat renvoie vrai, ainsi que son indice :
find ( seq quot -- index elt/f )
Si rien ne correspond, les deux valeurs valent f. Écarter l'indice avec nip puis utiliser unless* est la combinaison standard pour obtenir une valeur par défaut en cas d'absence.
reject : écarter les correspondancesreject (également dans sequences) est l'opposé de filter : il renvoie les éléments qui ne satisfont pas le prédicat.
Ton association de quartier te demande de gérer les inscriptions aux parcelles du jardin. L'état est stocké dans deux variables dynamiques :
registrations : un vecteur de tuples plot actuellement attribués à une personne.next-id : l'entier à utiliser pour la prochaine inscription.Le tuple plot prend deux emplacements :
| emplacement | type |
|---|---|
id |
entier |
registered-to |
string |
Définis open-garden pour initialiser les variables dynamiques : un vecteur vide pour registrations, et 1 pour next-id. Définis ensuite list-registrations pour renvoyer le vecteur actuel de parcelles.
open-garden
list-registrations .
! => V{ }
Définis register pour prendre un nom sur la pile, construire un nouveau plot avec l'id disponible suivant, l'ajouter au vecteur registrations, incrémenter next-id de un, et renvoyer le nouveau 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" } } }
Les identifiants de parcelle doivent être uniques et croître même après une libération : next-id ne doit jamais réutiliser de valeur.
Définis release pour prendre un identifiant et supprimer l'entrée correspondante de registrations. Libérer un identifiant inconnu ne fait rien.
open-garden
"Emma" register drop
1 release
list-registrations .
! => V{ }
Définis get-registration pour prendre un identifiant et renvoyer la parcelle correspondante, ou le symbole not-found si aucune parcelle ne porte cet identifiant.
open-garden
"Emma" register drop
1 get-registration .
! => T{ plot { id 1 } { registered-to "Emma" } }
7 get-registration .
! => not-found
Définis find-by-name pour prendre un nom et renvoyer un vecteur de toutes les parcelles actuellement enregistrées à cette personne.
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" } } }
Inscris-toi sur Exercism pour apprendre et maîtriser Factor avec 47 concepts163 exercices, et un vrai mentorat humain, le tout gratuitement.