Tracks
/
Common Lisp
Common Lisp
/
Übungen
/
Lillys Lasagnereste
Lillys Lasagnereste

Lillys Lasagnereste

Lernübung

Einführung

Lambda-Liste

In Common Lisp wird die Argumentliste einer Funktion als 'Lambda-Liste' bezeichnet. Eine Lambda-Liste kann Argumente verschiedener Typen enthalten. Diese verschiedenen Typen werden mit 'Lambda-Listen-Schlüsselwörtern' gekennzeichnet, die alle mit & beginnen. Die am häufigsten verwendeten Typen sind optionale, Schlüsselwort- und Rest-Argumente. Alle Parameter in der Lambda-Liste, die auf ein bestimmtes Lambda-Listen-Schlüsselwort folgen, haben diesen Typ. Ein Lambda-Listen-Schlüsselwort darf in einer Lambda-Liste nur einmal verwendet werden.

Lambda-Listen werden auch in anderen Konstrukten verwendet, die später besprochen werden, zum Beispiel beim Destrukturieren und bei Makros.

Optionale Parameter

In Common Lisp kann eine Funktion Argumente haben, die optional sind. Diese werden in der Lambda-Liste durch das Lambda-Listen-Schlüsselwort &optional gekennzeichnet. Ein Parameter, der nicht angegeben wird, ist an den Wert nil gebunden. Gibt es mehrere optionale Parameter, werden sie der Reihe nach gebunden. Für optionale Parameter können Standardwerte angegeben werden. Schließlich kann für jeden optionalen Parameter ein Symbol angegeben werden, das an wahr oder falsch gebunden wird, je nachdem, ob der Aufrufer der Funktion diesen Parameter übergeben hat (dies wird als „supplied-p-Parameter“ bezeichnet).

(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))

Schlüsselwortparameter

In Common Lisp kann eine Funktion benannte Parameter haben (die als „Schlüsselwortparameter“ bezeichnet werden). Diese werden in der Lambda-Liste durch das Lambda-Listen-Schlüsselwort &key gekennzeichnet. Schlüsselwortparameter sind keine erforderlichen Parameter. Wie optionale Parameter können sie Standardwerte erhalten, außerdem Symbole, die an ihren Zustand „übergeben oder nicht“ gebunden werden.

Beim Aufruf einer Funktion mit Schlüsselwortparametern wird der Name des Parameters als Schlüsselwort vor den Parameterwert gesetzt. Der Aufrufer der Funktion kann Schlüsselwortparameter in beliebiger Reihenfolge angeben.

(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))

Beim Kombinieren von optionalen und Schlüsselwortparametern ist Vorsicht geboten, da der Schlüsselwortname und das Argument von optionalen Parametern verbraucht werden könnten:

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

Rest-Parameter

In Common Lisp kann eine Funktion einen Parameter haben, der den „Rest“ der Argumente enthält, nachdem alle erforderlichen oder optionalen Parameter verarbeitet wurden. Dieser Parameter wird durch das Lambda-Listen-Schlüsselwort &rest gekennzeichnet. Werden alle Argumente einer Funktion von anderen Parametertypen verwendet, wird der Rest-Parameter an eine leere Liste gebunden. Gibt es ungenutzte Argumente, wird der Rest-Parameter an eine Liste dieser Argumente gebunden.

(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))

Anleitung

Lilly, das Lisp-Alien, hat mit deiner Hilfe definitiv eine Menge Lasagne gemacht. Sie möchte sich mit ein paar Resten bei dir bedanken. Aber zuerst hat sie noch ein paar Anmerkungen zu einer der Funktionen, bei der du ihr geholfen hast.

In dieser Übung schreibst du noch ein paar Funktionen, die Lilly beim Kochen ihrer Lasagne helfen und ihr außerdem dabei helfen, die Reste mit dir aufzuteilen.

Du hast sechs Aufgaben: drei drehen sich darum, die bestehenden Lasagne-Kochfunktionen zu verbessern, und drei darum, die Reste aufzuteilen.

1. Mach preparation-time-in-minutes einfacher zu benutzen

Lilly sagt, dass sie die Benutzung deiner Funktion preparation-time-in-minutes etwas umständlich fand, weil sie vor dem Aufruf der Funktion alle Schichten zählen musste. Lilly fragt sich, ob du eine Version der Funktion schreiben könntest, die die Schichten (beliebig viele) als Argumente entgegennimmt und dann die Vorbereitungszeit danach berechnet, wie viele Schichten es waren. Zur Erinnerung: Eine einzelne Schicht braucht 19 Minuten Vorbereitung.

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

2. Erlaube, die erwartete Backzeit zu ändern

Während Lillys Elterneinheiten ihre Lasagne immer 337 Minuten lang gebacken haben, gibt Lilly zu, dass andere Lisp-Aliens ihre Lasagne gern länger oder kürzer backen.

Also wäre es gut, wenn remaining-minutes-in-oven nicht expected-minutes-in-oven verwenden würde, sondern stattdessen einen optionalen Parameter entgegennehmen könnte, der angibt, ob die Zeit länger, sehr lang, kürzer oder sehr kurz sein soll. Länger oder kürzer bedeutet, 100 zur Standardzeit von 337 zu addieren oder davon zu subtrahieren. Sehr lang oder sehr kurz bedeutet, 200 zu addieren oder zu subtrahieren. Wenn kein Parameter angegeben wird oder das Argument 'normal' ist, soll der Standardwert 337 zurückgegeben werden.

(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 erinnert sich an einen weiteren bevorzugten Kochstil

Gerade als du diese Änderung fertig hast, fällt Lilly ein, dass es einige Lisp-Aliens gibt, die wirklich kurze Backzeiten mögen. Im Grunde wollen sie sie roh. Also soll die Funktion jetzt 0 zurückgeben, wenn NIL das Argument war, und ansonsten dieselben Werte wie in der vorherigen Aufgabe.

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

4. Die Reste aufteilen

Jetzt, wo du geholfen hast, die Lasagne-Kochfunktionen zu verbessern, gibt es sehr viel Lasagne! Lilly möchte sich für all deine Hilfe erkenntlich zeigen und die Reste mit dir teilen. Du musst eine Funktion schreiben, die drei Parameter entgegennimmt: die Gesamtmenge der Reste, die Anzahl der Behälter für Reste, die Lilly in ihren Schränken gefunden hat, und die Anzahl, die du gefunden hast. Da die Suche nach Behältern für Reste unterschiedlich lange dauern kann und die genaue Menge der Reste erst feststeht, während du und Lilly eure Schränke durchsucht, seid ihr euch einig, dass die Funktion die Argumente in beliebiger Reihenfolge annehmen soll, damit du jede Zahl gleich notieren kannst, sobald sie feststeht. Wenn du alle drei Zahlen hast, schließt du einfach die Klammer des Funktionsaufrufs und wertest ihn aus.

Die Funktion soll die Menge der Reste zurückgeben, die übrig bleibt, also die Differenz zwischen dem Gewicht der Reste und der Gesamtzahl der Behälter, die du und Lilly gefunden habt.

(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. Annahmen treffen

Nach ein paar Hin-und-her-Wegen zu euren jeweiligen Schränken seid ihr euch einig, dass es großartig wäre, wenn die Funktion einfach annehmen könnte, dass du oder Lilly ... sagen wir 10 Behälter hättet, wenn nichts angegeben wird.

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

6. Standardmenge an Resten

Noch besser wäre es, wenn die Funktion annehmen könnte, dass genug Reste da sind, um in die Behälter zu passen. Wenn das Gewicht also nicht angegeben wird, soll die Funktion :just-split-it zurückgeben. Wenn das Gewicht jedoch angegeben wird und nil ist, soll sie :looks-like-someone-was-hungry zurückgeben.

(split-leftovers :human 5 :alien 5) ;; => :JUST-SPLIT-IT
(split-leftovers :weight NIL :human 5 :alien 5) ;; => :LOOKS-LIKE-SOMEONE-WAS-HUNGRY
Über GitHub bearbeiten Der Link öffnet sich in einem neuen Fenster oder Tab
Common Lisp Exercism

Bereit, mit Lillys Lasagnereste zu starten?

Melde dich bei Exercism an, um Common Lisp mit 27 Konzepte92 Übungen und echtem menschlichen Mentoring zu lernen und zu meistern, alles kostenlos.