Track
/
Factor
Factor
/
Esercizi
/
Luminare delle lasagne
Luminare delle lasagne

Luminare delle lasagne

Esercizio di apprendimento

Introduzione

Per le word brevi va bene destreggiarsi con lo stack. Quando una word ha diversi input e devi riferirti a ciascuno per nome, Factor offre i locals: binding con nome introdotti con :: (per le definizioni di word) o [| | ] (per le lambda inline).

:: ( inputs -- outputs )

Sostituisci : con :: e i nomi degli input diventano variabili locali nel corpo:

USING: locals ;

:: hypotenuse ( a b -- c )
    a a *
    b b *
    + sqrt ;

Confronta con la versione che destreggia lo stack:

! DOCTEST: SKIP   (shown for comparison — would re-define hypotenuse)
: hypotenuse ( a b -- c )
    [ sq ] bi@ + sqrt ;

Entrambe sono valide: i locals danno il meglio quando a un input si fa riferimento più di una volta o quando l'ordine delle operazioni non coincide naturalmente con l'ordine dello stack.

Associare valori intermedi con :>

All'interno di un corpo :: (o di una lambda inline [| ... |]), :> estrae un valore dallo stack dati e lo associa a un nuovo local:

value :> name      ! immutable binding
value :> name!     ! mutable binding (name! is its setter)

La forma immutabile è quella più comune: usala per dare un nome a un valore intermedio, così il corpo si legge dall'alto verso il basso:

USING: locals math ;

:: average-3 ( x y z -- avg )
    x y + z + :> sum
    sum 3 / ;

La variante con ! dichiara il local come mutabile; più avanti nel corpo, value name! (senza i due punti, senza :>) lo riassegna:

USING: kernel locals math math.order ;

:: clamped ( x lo hi -- y )
    x :> v!
    v lo < [ lo v! ] when
    v hi > [ hi v! ] when
    v ;

I locals mutabili danno il meglio all'interno delle word di iterazione che incontrerai negli esercizi successivi (ad esempio while).

[let: uno scope da qualsiasi punto

:> funziona solo all'interno di uno scope lessicale, che :: e [| ... |] creano automaticamente. Per introdurre locals da una word : semplice o dal listener, avvolgi il codice in [let ... ]:

[let code :> name code :> name ... body ]

Ogni :> name associa il valore in cima allo stack dell'espressione precedente; i binding sono visibili al resto della forma.

USING: locals math ;

[let 3 :> x 4 :> y x x * y y * + ] .   ! => 25

[let è il ponte tra le definizioni : in stile stack e i binding con nome: torna utile quando solo una parte di una word trae vantaggio dai locals.

Lambda: [| inputs | body ]

All'interno di una quotation, [| inputs | body ] introduce dei locals a cui il corpo può riferirsi per nome. È particolarmente utile con le word di ordine superiore come assoc-map, la cui quotation riceve sia key sia value:

USING: assocs locals ;

H{ { "a" 1 } { "b" 2 } }
[| key value | key value neg ] assoc-map .
! => H{ { "a" -1 } { "b" -2 } }

La stessa forma funziona con map-index (in [sequences][sequences]), la cui quotation riceve sia l'elemento sia il suo indice. I nomi chiariscono quale operando svolge quale ruolo:

USING: locals sequences ;

! Divide each element by its 1-based position:
{ 10 80 270 } [| elt i | elt i 1 + / ] map-index .
! => { 10 40 90 }

L'effetto stack della lambda è determinato dalla lista degli input e da ciò che produce il suo corpo.

Quando usare i locals

Ricorri ai locals quando:

  • Lo stesso input viene usato in più di un punto.
  • L'ordine naturale delle operazioni non coincide con l'ordine dello stack, e il dup/swap/rot necessario per sistemarlo è difficile da leggere.
  • Ti trovi dentro una lambda inline passata a una word di ordine superiore che ti fornisce più valori in una volta sola (come in assoc-map).

Quando il corpo è un flusso lineare e pulito, le definizioni : semplici restano più brevi e almeno altrettanto leggibili.

assoc-map: una voce alla volta

assoc-map (in assocs) applica una quotation a ogni voce di una collezione associativa:

assoc-map ( assoc quot: ( key value -- newkey newvalue ) -- newassoc )

Una lambda con locals è il modo più pulito per scrivere la quotation.

Torniamo su cond

Quando devi distinguere in base a f o a un valore numerico, cond è esattamente la forma giusta: non serve case, perché non c'è un singolo valore con cui confrontarsi.

Istruzioni

Sei di nuovo in cucina, a rifinire la ricetta delle lasagne da Le deliziose lasagne di Leah. Le attività di questo esercizio ti chiederanno di gestire più input contemporaneamente: ed è proprio in questi casi che le variabili locali, cioè i binding con nome di Factor, si rivelano preziose.

1. Determina lo stato di cottura

Definisci cooking-status in modo che prelevi una lettura del timer dallo stack e restituisca una stringa di stato.

  • 0 → "Lasagna is done."
  • f (il timer non è mai stato impostato) → "You forgot to set the timer."
  • qualsiasi altro numero → "Not done, please wait."
12 cooking-status .   ! => "Not done, please wait."
0 cooking-status .    ! => "Lasagna is done."
f cooking-status .    ! => "You forgot to set the timer."

2. Stima il tempo di preparazione

Definisci preparation-time in modo che prenda un array di nomi di strato e un numero medio di minuti per strato, e restituisca il tempo di preparazione totale.

{ "sauce" "noodles" "sauce" "meat" "mozzarella" "noodles" } 3 preparation-time .
! => 18

3. Calcola le quantità di noodles e sugo necessarie

Definisci quantities in modo che prenda un array di nomi di strato e restituisca due valori: i grammi di noodles necessari (50 g per ogni strato di noodles) e i litri di sugo necessari (1/5 di litro per ogni strato di sugo).

{ "sauce" "noodles" "sauce" "meat" "mozzarella" "noodles" } quantities .s
! => 100
! => 2/5

4. Aggiungi l'ingrediente segreto

Una persona amica ti manda un elenco di ingredienti: l'ultimo elemento è il suo ingrediente segreto. Definisci add-secret-ingredient in modo che prenda il suo elenco e il tuo e restituisca il tuo elenco con l'ingrediente segreto aggiunto in fondo.

{ "noodles" "sauce" "mozzarella" "kampot pepper" }
{ "noodles" "meat" "sauce" "mozzarella" }
add-secret-ingredient .
! => { "noodles" "meat" "sauce" "mozzarella" "kampot pepper" }

5. Ridimensiona la ricetta

La ricetta del libro di cucina produce due porzioni. Definisci scale-recipe in modo che prenda una ricetta (una hashtable che associa il nome di un ingrediente alla sua quantità) e un numero di porzioni obiettivo, e restituisca una nuova ricetta scalata di conseguenza.

H{ { "noodles" 200 } { "sauce" 1/2 } { "mozzarella" 1 } { "meat" 100 } }
4 scale-recipe .
! => H{ { "noodles" 400 } { "sauce" 1 } { "mozzarella" 2 } { "meat" 200 } }
Modifica tramite GitHub Il link si apre in una nuova finestra o scheda
Factor Exercism

Vuoi iniziare Luminare delle lasagne?

Iscriviti a Exercism per imparare e padroneggiare Factor con 47 concetti163 esercizi e il mentoring di persone reali, tutto gratis.