En Common Lisp, la liste d'arguments d'une fonction est appelée une liste lambda.
Une liste lambda peut contenir des arguments de différents types.
Ces différents types sont désignés par l'utilisation de mots-clés de liste lambda, qui commencent tous par &.
Les types les plus couramment utilisés sont les arguments optionnels, les arguments à mot-clé et les arguments restants.
Tout paramètre de la liste lambda qui suit un mot-clé de liste lambda donné sera de ce type.
Un mot-clé de liste lambda ne peut être utilisé qu'une seule fois dans une liste lambda.
Les listes lambda sont aussi utilisées dans d'autres constructions dont nous parlerons plus tard, comme la déstructuration et les macros.
En Common Lisp, une fonction peut avoir des arguments optionnels.
Ils sont désignés dans la liste lambda par le mot-clé de liste lambda &optional.
Un paramètre sera lié à la valeur nil s'il n'est pas spécifié.
S'il y a plusieurs paramètres optionnels, ils sont liés dans l'ordre.
On peut spécifier des valeurs par défaut pour les paramètres optionnels.
Enfin, on peut spécifier un symbole pour chaque paramètre optionnel, qui sera lié à vrai ou faux selon que ce paramètre a été fourni ou non par l'appelant de la fonction (c'est ce qu'on appelle le « paramètre 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))
En Common Lisp, une fonction peut avoir des paramètres nommés (appelés « paramètres à mot-clé »).
Ils sont désignés dans la liste lambda par le mot-clé de liste lambda &key.
Les paramètres à mot-clé ne sont pas des paramètres obligatoires.
Comme les paramètres optionnels, ils peuvent recevoir des valeurs par défaut et des symboles auxquels lier leur état « fourni ou non ».
Lorsqu'on appelle une fonction avec des paramètres à mot-clé, le nom du paramètre sous forme de mot-clé est placé devant la valeur du paramètre. L'appelant de la fonction peut spécifier les paramètres à mot-clé dans n'importe quel ordre.
(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))
Il faut faire attention lorsqu'on combine des paramètres optionnels et à mot-clé, car le nom du mot-clé et l'argument pourraient être consommés par les paramètres optionnels :
(defun could-be-confusing (&optional x y &key z) (list x y z))
(could-be-confusing :z 'huh?) ; => (:Z HUH? NIL)
En Common Lisp, une fonction peut avoir un paramètre qui contiendra le « reste » des arguments une fois tous les paramètres obligatoires ou optionnels traités.
Ce paramètre est désigné par le mot-clé de liste lambda &rest.
Si tous les arguments d'une fonction sont utilisés par d'autres types de paramètres, alors le paramètre restant sera lié à une liste vide.
S'il reste des arguments inutilisés, le paramètre restant sera lié à une liste contenant ces arguments.
(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, l'extraterrestre Lisp, a vraiment préparé beaucoup de lasagnes avec ton aide. Lilly veut te remercier en te donnant quelques restes. Mais d'abord, Lilly a quelques retours à te faire sur l'une des fonctions auxquelles tu as contribué.
Dans cet exercice, tu vas écrire d'autres fonctions pour aider Lilly à cuisiner ses lasagnes et aussi pour l'aider à partager les restes avec toi.
Tu as six tâches, trois sur l'amélioration des fonctions de cuisson des lasagnes existantes et trois sur le partage des restes.
preparation-time-in-minutes plus facile à utiliserLilly dit qu'elle a trouvé l'utilisation de ta fonction preparation-time-in-minutes un peu laborieuse, car elle devait compter toutes les couches avant d'utiliser les fonctions. Lilly se demande si tu pourrais créer une version de la fonction qui prend les couches (un nombre quelconque d'entre elles) comme arguments, puis calcule le temps de préparation en fonction du nombre de couches. Pour rappel, il faut 19 minutes pour préparer une seule couche.
(preparation-time 'sauce 'cheese 'right-handed-macaroni 'cheese 'sauce
'left-handed-macaroni 'sauce 'sauce 'cheese 'cheese)
;; => 190 (because there are 10 layers.)
Bien que les unités parentales de Lilly aient toujours cuit leurs lasagnes pendant 337 minutes, Lilly admet que d'autres extraterrestres Lisp aiment cuire leurs lasagnes plus ou moins longtemps.
Ce serait donc bien si remaining-minutes-in-oven n'utilisait pas expected-minutes-in-oven, mais pouvait plutôt prendre un paramètre optionnel pour définir si le temps doit être plus long, très long, plus court ou très court. Plus long ou plus court signifie ajouter ou soustraire 100 au temps par défaut de 337. Très long ou très court signifie ajouter ou soustraire 200. Si aucun paramètre n'est fourni, ou si l'argument est 'normal', alors la valeur par défaut de 337 doit être renvoyée.
(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
Juste après que tu as terminé cette modification, Lilly se souvient que certains extraterrestres Lisp aiment les temps de cuisson vraiment courts. En fait, ils la veulent crue. Maintenant, la fonction doit donc renvoyer 0 si NIL était l'argument, ainsi que les mêmes valeurs que dans la tâche précédente.
(remaining-minutes-in-oven) ;; => 337
(remaining-minutes-in-oven nil) ;; => 0
Maintenant que tu as aidé à améliorer les fonctions de cuisson des lasagnes, il y a beaucoup de lasagnes ! Lilly veut te remercier pour toute ton aide en partageant les restes avec toi. Tu vas devoir écrire une fonction qui prend trois paramètres : la quantité totale de restes, le nombre de contenants pour les restes que Lilly a trouvés dans ses placards et le même nombre pour toi. Comme la recherche de contenants pour les restes peut prendre des temps différents, et que la quantité exacte de restes peut être déterminée pendant que Lilly et toi fouillez vos placards respectifs, vous êtes tous les deux d'accord pour que la fonction prenne les arguments dans n'importe quel ordre, afin que tu puisses noter chaque nombre au fur et à mesure qu'il est déterminé. Quand tu as les trois nombres, tu peux simplement fermer la parenthèse de l'appel de fonction et l'évaluer.
La fonction doit renvoyer la quantité de restes qui restent, c'est-à-dire la différence entre le poids des restes et le nombre total de contenants que Lilly et toi avez trouvés.
(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
Après quelques allers-retours vers vos placards respectifs, vous convenez tous les deux que ce serait formidable si la fonction pouvait simplement supposer que toi ou Lilly avait... disons 10 contenants si ce n'était pas précisé.
(split-leftovers :weight 20 :human 5) ;; => 5
(split-leftovers :weight 20 :alien 5) ;; => 5
(split-leftovers :weight 20) ;; => 0
Encore mieux, la fonction pourrait supposer qu'il y a assez de restes pour remplir les contenants. Donc, si le poids n'est pas fourni, la fonction doit renvoyer :just-split-it. Cependant, si le poids est fourni et qu'il est nil, elle doit renvoyer :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
Inscris-toi sur Exercism pour apprendre et maîtriser Common Lisp avec 27 concepts92 exercices, et un vrai mentorat humain, le tout gratuitement.