Στην Common Lisp, η λίστα ορισμάτων μιας συνάρτησης είναι γνωστή ως 'λίστα lambda.
Μια λίστα lambda μπορεί να έχει ορίσματα διαφορετικών τύπων.
Αυτοί οι διαφορετικοί τύποι δηλώνονται με τη χρήση 'λέξεων-κλειδιών λίστας lambda', οι οποίες όλες ξεκινούν με &.
Οι πιο συχνά χρησιμοποιούμενοι τύποι είναι οι τύποι προαιρετικών ορισμάτων, ορισμάτων-λέξεων-κλειδιών και ορισμάτων rest.
Κάθε παράμετρος στη λίστα lambda μετά από μια συγκεκριμένη λέξη-κλειδί λίστας lambda θα είναι αυτού του τύπου.
Μια λέξη-κλειδί λίστας lambda μπορεί να χρησιμοποιηθεί μόνο μία φορά σε μια λίστα lambda.
Οι λίστες lambda χρησιμοποιούνται επίσης σε άλλες δομές που θα συζητηθούν αργότερα, όπως η αποδόμηση και οι μακροεντολές.
Στην Common Lisp, μια συνάρτηση μπορεί να έχει μερικά ορίσματα που είναι προαιρετικά.
Αυτά δηλώνονται στη λίστα lambda με τη λέξη-κλειδί λίστας lambda &optional.
Μια παράμετρος θα δεθεί με την τιμή nil αν δεν προσδιοριστεί.
Αν υπάρχουν πολλές προαιρετικές παράμετροι, δένονται με τη σειρά.
Μπορούν να προσδιοριστούν προεπιλεγμένες τιμές για τις προαιρετικές παραμέτρους.
Τέλος, μπορεί να προσδιοριστεί ένα σύμβολο για κάθε προαιρετική παράμετρο, το οποίο θα δεθεί με αληθής ή ψευδής ανάλογα με το αν αυτή η παράμετρος δόθηκε από αυτόν που κάλεσε τη συνάρτηση (αυτό αναφέρεται ως "παράμετρος 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))
Στην Common Lisp, μια συνάρτηση μπορεί να έχει ονομασμένες παραμέτρους (που αναφέρονται ως "παράμετροι με λέξεις-κλειδιά").
Αυτές δηλώνονται στη λίστα lambda με τη λέξη-κλειδί λίστας lambda &key.
Οι παράμετροι με λέξεις-κλειδιά δεν είναι υποχρεωτικές παράμετροι.
Όπως οι προαιρετικές παράμετροι, μπορούν να τους δοθούν προεπιλεγμένες τιμές και σύμβολα για να δεθούν με την κατάσταση 'δόθηκε ή όχι'.
Όταν καλείς μια συνάρτηση με παραμέτρους με λέξεις-κλειδιά, το όνομα της παραμέτρου ως λέξη-κλειδί χρησιμοποιείται μπροστά από την τιμή της παραμέτρου. Οι παράμετροι με λέξεις-κλειδιά μπορούν να προσδιοριστούν από αυτόν που καλεί τη συνάρτηση με οποιαδήποτε σειρά.
(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))
Χρειάζεται προσοχή όταν συνδυάζεις προαιρετικές παραμέτρους με παραμέτρους με λέξεις-κλειδιά, καθώς το όνομα της λέξης-κλειδιού και το όρισμα θα μπορούσαν να καταναλωθούν από τις προαιρετικές παραμέτρους:
(defun could-be-confusing (&optional x y &key z) (list x y z))
(could-be-confusing :z 'huh?) ; => (:Z HUH? NIL)
Στην Common Lisp, μια συνάρτηση μπορεί να έχει μια παράμετρο που θα περιέχει το "υπόλοιπο" των ορισμάτων αφού επεξεργαστούν τυχόν υποχρεωτικές ή προαιρετικές παράμετροι.
Αυτή η παράμετρος δηλώνεται με τη λέξη-κλειδί λίστας lambda &rest.
Αν όλα τα ορίσματα μιας συνάρτησης χρησιμοποιούνται από άλλους τύπους παραμέτρων, τότε η παράμετρος rest θα δεθεί με μια κενή λίστα.
Αν υπάρχουν ορίσματα που δεν χρησιμοποιήθηκαν, τότε η παράμετρος rest θα δεθεί με μια λίστα αυτών των ορισμάτων.
(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))
Η Lilly, η εξωγήινη της Lisp, έχει φτιάξει σίγουρα πολλά λαζάνια με τη βοήθειά σου. Θέλει να σε ευχαριστήσει δίνοντάς σου μερικά υπολείμματα. Πρώτα όμως έχει κάποια σχόλια για μία από τις συναρτήσεις με τις οποίες τη βοήθησες.
Σε αυτή την άσκηση θα γράψεις μερικές ακόμα συναρτήσεις για να βοηθήσεις τη Lilly στο μαγείρεμα των λαζάνια, αλλά και για να τη βοηθήσεις να μοιραστείτε τα υπολείμματα.
Έχεις έξι εργασίες: τρεις για τη βελτίωση των υπαρχουσών συναρτήσεων μαγειρέματος των λαζάνια και τρεις για τη μοιρασιά των υπολειμμάτων.
preparation-time-in-minutes πιο εύκολη στη χρήσηΗ Lilly λέει ότι βρήκε λίγο άβολη τη χρήση της preparation-time-in-minutes, αφού έπρεπε να μετρήσει όλες τις στρώσεις πριν χρησιμοποιήσει τις συναρτήσεις. Η Lilly αναρωτιέται αν θα μπορούσες να φτιάξεις μια εκδοχή της συνάρτησης που να παίρνει τις στρώσεις (όσες κι αν είναι) ως ορίσματα και μετά να υπολογίζει τον χρόνο προετοιμασίας με βάση το πόσες στρώσεις υπήρχαν. Υπενθύμιση: χρειάζονται 19 λεπτά για να προετοιμαστεί μία μόνο στρώση.
(preparation-time 'sauce 'cheese 'right-handed-macaroni 'cheese 'sauce
'left-handed-macaroni 'sauce 'sauce 'cheese 'cheese)
;; => 190 (because there are 10 layers.)
Ενώ οι γονικές μονάδες της Lilly έψηναν πάντα τα λαζάνια τους για 337 λεπτά, η Lilly παραδέχεται ότι άλλοι εξωγήινοι της Lisp προτιμούν να ψήνουν τα λαζάνια τους για μεγαλύτερα ή μικρότερα χρονικά διαστήματα.
Θα ήταν λοιπόν καλό η remaining-minutes-in-oven να μη χρησιμοποιεί την expected-minutes-in-oven, αλλά να μπορεί να παίρνει μια προαιρετική παράμετρο που να ορίζει αν ο χρόνος πρέπει να είναι "longer", "very long", "shorter" ή "very short". Το "longer" ή το "shorter" σημαίνει πρόσθεσε ή αφαίρεσε 100 από τον προεπιλεγμένο χρόνο των 337. Το "very long" ή το "very short" σημαίνει πρόσθεσε ή αφαίρεσε 200. Αν δεν δοθεί καμία παράμετρος, ή το όρισμα είναι "normal", τότε θα πρέπει να επιστρέφεται η προεπιλεγμένη τιμή 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
Μόλις τελείωσες αυτή την αλλαγή, η Lilly θυμήθηκε ότι υπάρχουν μερικοί εξωγήινοι της Lisp που θέλουν πραγματικά σύντομους χρόνους ψησίματος. Στην ουσία το θέλουν ωμό. Έτσι τώρα η συνάρτηση θα πρέπει να επιστρέφει 0 αν το όρισμα ήταν NIL, καθώς και τις ίδιες τιμές όπως στην προηγούμενη εργασία.
(remaining-minutes-in-oven) ;; => 337
(remaining-minutes-in-oven nil) ;; => 0
Τώρα που βοήθησες να βελτιωθούν οι συναρτήσεις μαγειρέματος των λαζάνια, υπάρχουν πάρα πολλά λαζάνια! Η Lilly θέλει να σε ανταμείψει για όλη τη βοήθειά σου μοιράζοντας τα υπολείμματα μαζί σου. Θα χρειαστεί να γράψεις μια συνάρτηση που παίρνει τρεις παραμέτρους: τη συνολική ποσότητα των υπολειμμάτων, τον αριθμό των δοχείων για υπολείμματα που βρήκε η Lilly στα ντουλάπια της και τον ίδιο αριθμό για σένα. Επειδή η αναζήτηση δοχείων για υπολείμματα μπορεί να πάρει διαφορετικό χρόνο, και η ακριβής ποσότητα των υπολειμμάτων μπορεί να καθοριστεί όσο εσύ και η Lilly ψάχνετε στα ντουλάπια σας, συμφωνείτε και οι δύο ότι η συνάρτηση θα πρέπει να δέχεται τα ορίσματα με οποιαδήποτε σειρά, ώστε να μπορείς να σημειώνεις τον κάθε αριθμό μόλις τον καθορίζεις. Όταν έχεις και τους τρεις αριθμούς, μπορείς απλώς να κλείσεις την παρένθεση στην κλήση της συνάρτησης και να την αξιολογήσεις.
Η συνάρτηση θα πρέπει να επιστρέφει την ποσότητα των υπολειμμάτων που περισσεύουν, δηλαδή τη διαφορά ανάμεσα στο βάρος των υπολειμμάτων και στον συνολικό αριθμό των δοχείων που βρήκατε εσύ και η Lilly.
(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
Ύστερα από μερικές διαδρομές πήγαινε-έλα στα αντίστοιχα ντουλάπια σας, συμφωνείτε και οι δύο ότι θα ήταν υπέροχο αν η συνάρτηση μπορούσε απλώς να υποθέσει ότι εσύ ή η Lilly είχατε... ας πούμε 10 δοχεία, αν δεν είχε διευκρινιστεί.
(split-leftovers :weight 20 :human 5) ;; => 5
(split-leftovers :weight 20 :alien 5) ;; => 5
(split-leftovers :weight 20) ;; => 0
Ακόμα καλύτερα, η συνάρτηση θα μπορούσε να υποθέσει ότι υπάρχουν αρκετά υπολείμματα για να γεμίσουν τα δοχεία. Έτσι, αν δεν δοθεί το βάρος, η συνάρτηση θα πρέπει να επιστρέφει :just-split-it. Ωστόσο, αν δοθεί το βάρος και είναι nil, τότε θα πρέπει να επιστρέφει :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
Γράψου στο Exercism για να μάθεις και να κατακτήσεις Common Lisp με 27 έννοιες92 ασκήσεις και πραγματική καθοδήγηση από ανθρώπους, όλα δωρεάν.