Em Common Lisp, a lista de argumentos de uma função é conhecida como «lista lambda».
Uma lista lambda pode ter argumentos de tipos diferentes.
Esses tipos diferentes são designados com o uso de «palavras-chave de lista lambda», que começam todas por &.
Os tipos mais usados são os argumentos opcionais, os argumentos de palavra-chave e os argumentos restantes.
Todos os parâmetros da lista lambda que vêm a seguir a uma determinada palavra-chave de lista lambda são desse tipo.
Uma palavra-chave de lista lambda só pode ser usada uma vez numa lista lambda.
As listas lambda também são usadas noutras construções de que falaremos mais adiante, como a desestruturação e as macros.
Em Common Lisp, uma função pode ter alguns argumentos que são opcionais.
Estes são designados na lista lambda pela palavra-chave de lista lambda &optional.
Um parâmetro fica ligado ao valor nil se não for especificado.
Se houver vários parâmetros opcionais, são ligados por ordem.
É possível especificar valores predefinidos para os parâmetros opcionais.
Por fim, pode especificar-se um símbolo para cada parâmetro opcional, que fica ligado a verdadeiro ou falso consoante esse parâmetro tenha ou não sido fornecido por quem chama a função (a isto chama-se «parâmetro 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))
Em Common Lisp, uma função pode ter parâmetros com nome (designados por «parâmetros de palavra-chave»).
Estes são designados na lista lambda pela palavra-chave de lista lambda &key.
Os parâmetros de palavra-chave não são parâmetros obrigatórios.
Tal como os parâmetros opcionais, podem receber valores predefinidos e símbolos aos quais ligar o seu estado de «fornecido ou não».
Quando chamas uma função com parâmetros de palavra-chave, o nome do parâmetro é usado como palavra-chave, à frente do valor do parâmetro. Quem chama a função pode especificar os parâmetros de palavra-chave por qualquer ordem.
(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))
Tem cuidado ao combinar parâmetros opcionais com parâmetros de palavra-chave, porque os parâmetros opcionais podem consumir o nome da palavra-chave e o argumento:
(defun could-be-confusing (&optional x y &key z) (list x y z))
(could-be-confusing :z 'huh?) ; => (:Z HUH? NIL)
Em Common Lisp, uma função pode ter um parâmetro que vai conter o «resto» dos argumentos, depois de processados os parâmetros obrigatórios ou opcionais.
Este parâmetro é designado pela palavra-chave de lista lambda &rest.
Se todos os argumentos de uma função forem usados pelos outros tipos de parâmetros, o parâmetro restante fica ligado a uma lista vazia.
Se houver argumentos não usados, o parâmetro restante fica ligado a uma lista com esses argumentos.
(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, a alienígena do Lisp, fez sem dúvida muita lasanha com a tua ajuda. Quer agradecer-te dando-te algumas sobras. Mas primeiro tem alguns comentários sobre uma das funções em que a ajudaste.
Neste exercício vais escrever mais algumas funções para ajudar a Lilly a cozinhar a lasanha e também para a ajudares a dividir as sobras contigo.
Tens seis tarefas: três sobre melhorar as funções existentes de cozinhar lasanha e três sobre dividir as sobras.
preparation-time-in-minutes mais fácil de usarA Lilly diz que achou um pouco estranho usar a tua preparation-time-in-minutes, porque tinha de contar todas as camadas antes de usar as funções. A Lilly pergunta se podias fazer uma versão da função que recebe as camadas (quantas forem) como argumentos e depois calcula o tempo de preparação com base no número de camadas que houver. Como lembrete, preparar uma única camada leva 19 minutos.
(preparation-time 'sauce 'cheese 'right-handed-macaroni 'cheese 'sauce
'left-handed-macaroni 'sauce 'sauce 'cheese 'cheese)
;; => 190 (because there are 10 layers.)
Embora as unidades parentais da Lilly cozinhem sempre a lasanha durante 337 minutos, a Lilly admite que outros alienígenas do Lisp gostam de cozinhar a lasanha durante mais ou menos tempo.
Por isso, seria bom que remaining-minutes-in-oven não usasse expected-minutes-in-oven, mas que em vez disso pudesse receber um parâmetro opcional para definir se o tempo deve ser mais longo, muito longo, mais curto ou muito curto. Mais longo ou mais curto significa somar ou subtrair 100 ao tempo predefinido de 337. Muito longo ou muito curto significa somar ou subtrair 200. Se não for fornecido nenhum parâmetro, ou se o argumento for 'normal', deve devolver-se o valor predefinido de 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
Pouco depois de teres acabado essa alteração, a Lilly lembra-se de que há alguns alienígenas do Lisp que gostam de tempos de cozedura mesmo curtos. Na prática, querem-na crua. Por isso, agora a função deve devolver 0 se o argumento for NIL, além dos mesmos valores da tarefa anterior.
(remaining-minutes-in-oven) ;; => 337
(remaining-minutes-in-oven nil) ;; => 0
Agora que ajudaste a melhorar as funções de cozinhar lasanha, há imensa lasanha! A Lilly quer compensar-te por toda a tua ajuda, dividindo as sobras contigo. Vais precisar de escrever uma função que recebe três parâmetros: a quantidade total de sobras, o número de recipientes para sobras que a Lilly encontrou nos armários dela e o mesmo para ti. Como procurar recipientes para sobras pode demorar tempos diferentes, e a quantidade exata de sobras pode ser determinada enquanto tu e a Lilly estão a procurar nos armários, concordam os dois que a função deve receber os argumentos em qualquer ordem, para que possas ir escrevendo cada número à medida que é determinado. Quando tiveres os três números, basta fechar o parêntese da chamada da função e avaliá-la.
A função deve devolver a quantidade de sobras que sobram, ou seja, a diferença entre o peso das sobras e o número total de recipientes que tu e a Lilly encontraram.
(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
Depois de algumas idas e voltas aos vossos respetivos armários, concordam os dois que seria ótimo se a função pudesse simplesmente assumir que tu ou a Lilly tinham... digamos, 10 recipientes, caso não fosse especificado.
(split-leftovers :weight 20 :human 5) ;; => 5
(split-leftovers :weight 20 :alien 5) ;; => 5
(split-leftovers :weight 20) ;; => 0
Ainda melhor, a função poderia assumir que há sobras suficientes para caberem nos recipientes. Por isso, se o peso não for fornecido, a função deve devolver :just-split-it. Contudo, se o peso for fornecido e for nil, deve devolver :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
Inscreve-te no Exercism para aprenderes e dominares Common Lisp com 27 conceitos92 exercícios, e mentoria humana real, tudo grátis.