Track
/
Common Lisp
Common Lisp
/
Esercizi
/
Gli avanzi della lasagna di Lilly
Gli avanzi della lasagna di Lilly

Gli avanzi della lasagna di Lilly

Esercizio di apprendimento

Introduzione

Lista lambda

In Common Lisp la lista degli argomenti di una funzione è nota come «lista lambda». Una lista lambda può avere argomenti di tipi diversi. Questi diversi tipi sono indicati con l'uso di parole chiave di lista lambda, che iniziano tutte con &. I tipi più comuni sono i parametri opzionali, keyword e rest. Ogni parametro nella lista lambda dopo una particolare parola chiave di lista lambda sarà di quel tipo. Una parola chiave di lista lambda può essere usata una sola volta in una lista lambda.

Le liste lambda si usano anche in altri costrutti di cui parleremo più avanti, come la destrutturazione e le macro.

Parametri opzionali

In Common Lisp una funzione può avere alcuni argomenti opzionali. Questi sono indicati nella lista lambda dalla parola chiave di lista lambda &optional. Un parametro sarà legato al valore nil se non viene specificato. Se ci sono diversi parametri opzionali, vengono legati in ordine. Si possono specificare valori predefiniti per i parametri opzionali. Infine, per ogni parametro opzionale si può specificare un simbolo che sarà legato a vero o falso a seconda che quel parametro sia stato fornito o meno da chi chiama la funzione (questo è detto «parametro supplied-p»).

(defun optional-parameters (&optional x (y 'default) (z nil z-supplied-p))
  (list x y (if z-supplied-p (list :z-was-supplied z)
                             (list :z-was-not-supplied z))))

(optional-parameters)          ; => (NIL DEFAULT (:Z-WAS-NOT-SUPPLIED NIL))
(optional-parameters 5 nil 10) ; => (5 NIL (:Z-WAS-SUPPLIED 10))

Parametri keyword

In Common Lisp una funzione può avere parametri con nome (detti «parametri keyword»). Questi sono indicati nella lista lambda dalla parola chiave di lista lambda &key. I parametri keyword non sono parametri obbligatori. Come i parametri opzionali, possono avere valori predefiniti e simboli a cui legare il loro stato di «fornito o no».

Quando si chiama una funzione con parametri keyword, il nome del parametro come keyword viene usato davanti al valore del parametro. I parametri keyword possono essere specificati da chi chiama la funzione in qualsiasi ordine.

(defun keyword-parameters (&key x (y 'default) (z nil z-supplied-p))
  (list x y (if z-supplied-p (list :z-was-supplied z)
                             (list :z-was-not-supplied z))))

(keyword-parameters)            ; => (NIL DEFAULT (:Z-WAS-NOT-SUPPLIED NIL))
(keyword-parameters :y 5)       ; => (NIL 5 (:Z-WAS-NOT-SUPPLIED NIL))
(keyword-parameters :z 10 :x 5) ; => (5 DEFAULT (:Z-WAS-SUPPLIED 10))

Bisogna fare attenzione quando si combinano parametri opzionali e keyword, perché il nome e l'argomento keyword potrebbero essere consumati dai parametri opzionali:

(defun could-be-confusing (&optional x y &key z) (list x y z))
(could-be-confusing :z 'huh?) ; => (:Z HUH? NIL)

Parametri rest

In Common Lisp una funzione può avere un parametro che conterrà il «resto» degli argomenti dopo che i parametri obbligatori o opzionali sono stati elaborati. Questo parametro è indicato dalla parola chiave di lista lambda &rest. Se tutti gli argomenti di una funzione vengono usati da altri tipi di parametri, il parametro rest sarà legato a una lista vuota. Se ci sono argomenti non usati, il parametro rest sarà legato a una lista di quegli argomenti.

(defun rest-of-it (req &optional opt &rest rest) (list req opt rest))
(rest-of-it 1)         ; => (1 NIL NIL)
(rest-of-it 1 2)       ; => (1 2 NIL)
(rest-of-it 1 2 3)     ; => (1 2 (3))
(rest-of-it 1 2 3 4 5) ; => (1 2 (3 4 5))

Istruzioni

Lilly l'alieno Lisp ha decisamente preparato molta lasagna con il tuo aiuto. Vuole ringraziarti dandoti degli avanzi. Ma prima ha un feedback su una delle funzioni su cui l'hai aiutata.

In questo esercizio scriverai altre funzioni per aiutare Lilly a cucinare la lasagna e a dividere con te gli avanzi.

Hai sei compiti: tre per migliorare le funzioni di cottura della lasagna già esistenti e tre per dividere gli avanzi.

1. Rendi preparation-time-in-minutes più facile da usare

Lilly dice che usare la tua preparation-time-in-minutes era un po' scomodo, perché doveva contare tutti gli strati prima di usare le funzioni. Lilly si chiede se potresti creare una versione della funzione che prende gli strati (un numero qualsiasi) come argomenti e calcola il tempo di preparazione in base a quanti strati ci sono. Come promemoria, preparare un singolo strato richiede 19 minuti.

(preparation-time 'sauce 'cheese 'right-handed-macaroni 'cheese 'sauce
                  'left-handed-macaroni 'sauce 'sauce 'cheese 'cheese)
                  ;; => 190 (because there are 10 layers.)

2. Permetti di cambiare il tempo previsto in forno

Anche se le unità genitoriali di Lilly cucinavano sempre la loro lasagna per 337 minuti, Lilly ammette che altri alieni Lisp preferiscono cuocere la loro lasagna per un tempo più lungo o più breve.

Quindi sarebbe bene se remaining-minutes-in-oven non usasse expected-minutes-in-oven, ma potesse accettare un parametro opzionale per definire se il tempo deve essere più lungo, molto lungo, più breve o molto breve. Più lungo o più breve significa aggiungere o sottrarre 100 al tempo predefinito di 337. Molto lungo o molto breve significa aggiungere o sottrarre 200. Se non viene fornito alcun parametro, o se l'argomento è 'normal', allora dovrebbe essere restituito il valore predefinito di 337.

(remaining-minutes-in-oven)             ;; => 337
(remaining-minutes-in-oven :normal)     ;; => 337
(remaining-minutes-in-oven :shorter)    ;; => 237
(remaining-minutes-in-oven :very-short) ;; => 137
(remaining-minutes-in-oven :longer)     ;; => 437
(remaining-minutes-in-oven :very-long)  ;; => 537

3. Lilly ricorda un altro stile di cottura preferito

Subito dopo che hai completato quella modifica, Lilly ricorda che ci sono alcuni alieni Lisp a cui piacciono tempi di cottura davvero brevi. In pratica la vogliono cruda. Quindi ora la funzione dovrebbe restituire 0 se l'argomento era NIL, oltre agli stessi valori del compito precedente.

(remaining-minutes-in-oven)     ;; => 337
(remaining-minutes-in-oven nil) ;; => 0

4. Dividere gli avanzi

Ora che hai contribuito a migliorare le funzioni di cottura della lasagna, c'è moltissima lasagna! Lilly vuole ricompensarti per tutto il tuo aiuto dividendo con te gli avanzi. Dovrai scrivere una funzione che accetta tre parametri: la quantità totale di avanzi, il numero di contenitori per gli avanzi che Lilly ha trovato nelle sue dispense e lo stesso per te. Dato che cercare i contenitori per gli avanzi può richiedere tempi diversi, e la quantità esatta di avanzi potrebbe essere determinata mentre tu e Lilly siete fuori a cercare nelle dispense, concordate entrambi che la funzione dovrebbe accettare gli argomenti in qualsiasi ordine, così, man mano che ogni numero viene determinato, puoi annotarlo. Quando hai tutti e tre i numeri, puoi semplicemente chiudere la parentesi nella chiamata alla funzione e valutarla.

La funzione dovrebbe restituire la quantità di avanzi che avanza, cioè la differenza tra il peso degli avanzi e il numero totale di contenitori che tu e Lilly avete trovato.

(split-leftovers :weight 20 :human 10 :alien 5) ;; => 5
(split-leftovers :weight 20 :alien 10 :human 2) ;; => 8
(split-leftovers :alien 12 :weight 20 :human 4) ;; => 4

5. Fare supposizioni

Dopo qualche viaggio avanti e indietro verso le rispettive dispense, concordate entrambi che sarebbe fantastico se la funzione potesse semplicemente presumere che tu o Lilly aveste... diciamo 10 contenitori, se non fosse specificato.

(split-leftovers :weight 20 :human 5) ;; => 5
(split-leftovers :weight 20 :alien 5) ;; => 5
(split-leftovers :weight 20)          ;; => 0

6. Quantità standard di avanzi

Ancora meglio, la funzione potrebbe presumere che ci siano abbastanza avanzi da riempire i contenitori. Quindi, se il peso non viene fornito, la funzione dovrebbe restituire :just-split-it. Tuttavia, se il peso viene fornito ed è nil, allora dovrebbe restituire :looks-like-someone-was-hungry.

(split-leftovers :human 5 :alien 5) ;; => :JUST-SPLIT-IT
(split-leftovers :weight NIL :human 5 :alien 5) ;; => :LOOKS-LIKE-SOMEONE-WAS-HUNGRY
Modifica tramite GitHub Il link si apre in una nuova finestra o scheda
Common Lisp Exercism

Vuoi iniziare Gli avanzi della lasagna di Lilly?

Iscriviti a Exercism per imparare e padroneggiare Common Lisp con 27 concetti92 esercizi e il mentoring di persone reali, tutto gratis.