Parcours
/
Factor
Factor
/
Exercices
/
Cueillette au jardin
Cueillette au jardin

Cueillette au jardin

Exercice d'apprentissage

Introduction

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 ;

Définir et lire

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.

Liaisons à portée limitée avec 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.

Les vecteurs mutables

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 correspondant

find (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 correspondances

reject (également dans sequences) est l'opposé de filter : il renvoie les éléments qui ne satisfont pas le prédicat.

Instructions

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

1. Ouvre le jardin et liste ses inscriptions

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{ }

2. Enregistre une parcelle

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.

3. Libère une parcelle

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{ }

4. Récupère une parcelle enregistrée

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

5. Trouve des parcelles par nom

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" } } }
Modifie via GitHub Le lien s'ouvre dans une nouvelle fenêtre ou un nouvel onglet
Factor Exercism

Prêt à commencer Cueillette au jardin ?

Inscris-toi sur Exercism pour apprendre et maîtriser Factor avec 47 concepts163 exercices, et un vrai mentorat humain, le tout gratuitement.