Parcours
/
Factor
Factor
/
Exercices
/
Jus mélangés
Jus mélangés

Jus mélangés

Exercice d'apprentissage

Introduction

Certaines boucles ne peuvent pas s'écrire comme « fais ceci pour chaque élément d'une séquence » : il faut continuer jusqu'à ce qu'une condition bascule. Le combinateur while de Factor est l'outil qu'il te faut pour ça : une quotation prédicat qui demande « on continue ? », une quotation corps qui effectue le travail d'une étape, et while répète jusqu'à ce que le prédicat renvoie f.

while et until

while ( pred body -- )
until ( pred body -- )

pred et body sont tous deux des quotations qui opèrent sur la pile de données. while continue de boucler tant que pred laisse une valeur vraie ; until fait l'inverse : il continue de boucler jusqu'à ce que pred devienne vrai (autrement dit, tant que pred laisse f).

Une même valeur d'état peut circuler sur la pile entre le prédicat et le corps :

USING: kernel math ;

5 [ dup 0 > ] [ dup . 1 - ] while drop
! prints 5 4 3 2 1

Déroulons le fil : le prédicat dup 0 > jette un œil au compteur en cours ; le corps dup . 1 - l'affiche puis le décrémente ; la boucle s'arrête dès que le compteur atteint 0. Le drop final retire ce dernier 0 de la pile.

while* — garder la valeur du prédicat

while* ( pred body -- )

while se contente de regarder si son prédicat a laissé une valeur vraie : il jette la valeur elle-même avant d'exécuter le corps. while*, lui, transmet cette valeur au corps. Utilise-le dès que le test et le corps veulent la même valeur fraîchement calculée : une recherche, une correspondance, un jeton analysé.

Ici, chaque nom pointe vers le suivant dans une chaîne. Le prédicat recherche le nom courant ; tant qu'il existe un nom suivant, while* le transmet au corps, qui l'affiche et repart de là :

USING: assocs kernel locals prettyprint ;

:: print-chain ( start links -- )
    start [ links at ] [ dup . ] while* ;

"a" H{ { "a" "b" } { "b" "c" } } print-chain
! prints "b" then "c"

links at renvoie le nom suivant, ou f dès qu'un nom n'a pas d'entrée. Tant qu'il renvoie un nom, while* le donne à dup ., qui l'affiche et le laisse comme clé pour la recherche suivante. Le premier f arrête la boucle : while* le jette, donc il ne reste rien.

Plusieurs valeurs d'état avec des variables locales

Quand la boucle transporte plus d'une valeur, par exemple une liste restante et un total cumulé, le jonglage avec la pile devient bruyant. Le motif le plus propre consiste à déclarer des variables locales avec :: :

:: word ( inputs -- outputs ) ... ;

À l'intérieur du corps, :> name! ajoute une variable locale mutable ; les lectures utilisent name, les écritures utilisent name! (qui consomme la nouvelle valeur sur la pile et l'affecte).

USING: kernel locals math sequences ;

! Walk a sequence from the front, summing as we go
:: sum-by-hand ( seq -- total )
    seq :> rest!
    0   :> running!
    [ rest empty? ] [
        rest unclip [ rest! ] dip
        running + running!
    ] until
    running ;

Deux variables locales ici (rest et running) couvrent tout l'état de la boucle : le prédicat les inspecte, le corps les met à jour, et la valeur finale est renvoyée à la fin.

unclip ( seq -- rest first ) détache le premier élément d'une séquence, ce qui est pratique quand la boucle veut consommer une liste élément par élément. La forme [ rest! ] dip met à jour rest avec la queue restante tout en laissant l'élément fraîchement dépilé sur la pile à la disposition du corps.

Instructions

Ton amie Li Mei tient un bar à jus où elle vend de délicieux jus de fruits mélangés. Tu es un client régulier de sa boutique et tu as réalisé que tu pourrais lui faciliter un peu la vie avec du code Factor.

1. Détermine combien de temps il faut pour mixer un jus

Li Mei aime dire à ses clients à l'avance combien de temps ils devront attendre pour un jus de la carte. Elle a du mal à se rappeler les chiffres exacts, car le temps nécessaire pour mixer les jus varie :

  • "Pure Strawberry Joy" prend 0.5 minute.
  • "Energizer" et "Green Garden" prennent 1.5 minute chacun.
  • "Tropical Island" prend 3 minutes.
  • "All or Nothing" prend 5 minutes.
  • Toutes les autres boissons (par exemple les offres spéciales) prennent 2.5 minutes.

Définis time-to-mix-juice pour qu'elle retire de la pile le nom d'un jus et renvoie le nombre de minutes qu'il faut pour mixer cette boisson.

"Tropical Island" time-to-mix-juice .
! => 3

"Berries & Lime" time-to-mix-juice .
! => 2.5

2. Réapprovisionne la réserve de quartiers de citron vert

Beaucoup des créations de Li Mei contiennent des quartiers de citron vert, que ce soit comme ingrédient ou pour la décoration. Aussi, quand elle commence son service le matin, elle doit s'assurer que la réserve de quartiers de citron vert est bien pleine pour la journée à venir.

Définis wedges-from-lime pour qu'elle prenne une taille de citron vert et renvoie le nombre de quartiers qu'il produit : 6 pour un citron vert "small", 8 pour un "medium", 10 pour un "large".

Définis ensuite limes-to-cut pour qu'elle prenne le nombre de quartiers needed et un tableau de limes (des tailles de citron vert, dans l'ordre de découpe). Elle découpe les citrons verts un par un, dans l'ordre, et s'arrête dès qu'elle a assez de quartiers ou qu'elle n'a plus de citrons verts. Renvoie le nombre de citrons verts qu'il a fallu découper.

25 { "small" "small" "large" "medium" "small" } limes-to-cut .
! => 4

3. Liste les temps de mixage de chaque commande dans la file d'attente

Li Mei aime garder une trace du temps qu'il faudra pour mixer les commandes des clients qui attendent.

Définis order-times pour qu'elle prenne un tableau de commandes et renvoie un tableau contenant le temps de mixage de chaque commande, dans le même ordre.

{ "Energizer" "Tropical Island" } order-times .
! => { 1.5 3 }

4. Termine le service

Li Mei travaille toujours jusqu'à 15 h. Ensuite, son employé Dmitry prend le relais. Il arrive souvent que des boissons aient été commandées mais pas encore préparées quand le service de Li Mei se termine ; c'est alors Dmitry qui prépare les jus restants.

Pour faciliter la passation, définis remaining-orders pour qu'elle prenne le nombre de minutes time-left du service de Li Mei et un tableau d'orders (des jus qui ont été commandés mais pas encore préparés). Renvoie les commandes que Dmitry devra prendre en charge, c'est-à-dire celles que Li Mei n'a pas le temps de commencer avant la fin de son service.

time-left est toujours supérieur à 0. Les commandes sont préparées dans l'ordre dans lequel elles apparaissent dans le tableau. Une fois que Li Mei a commencé un jus, elle le termine toujours, même si elle doit travailler un peu plus longtemps. Si elle a le temps de commencer toutes les commandes, renvoie un tableau vide.

5 { "Energizer" "All or Nothing" "Green Garden" } remaining-orders .
! => { "Green Garden" }
Modifie via GitHub Le lien s'ouvre dans une nouvelle fenêtre ou un nouvel onglet
Factor Exercism

Prêt à commencer Jus mélangés ?

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