Rutas
/
Common Lisp
Common Lisp
/
Ejercicios
/
Las sobras de la lasaña de Lilly
Las sobras de la lasaña de Lilly

Las sobras de la lasaña de Lilly

Ejercicio de aprendizaje

Introducción

Lista lambda

En Common Lisp, la lista de argumentos de una función se conoce como 'lista lambda'. Una lista lambda puede tener argumentos de distintos tipos. Estos distintos tipos se designan con el uso de 'palabras clave de lista lambda', que siempre empiezan por &. Los tipos más usados son los argumentos opcionales, de palabra clave y rest. Todos los parámetros de la lista lambda que aparezcan después de una palabra clave de lista lambda concreta serán de ese tipo. Una palabra clave de lista lambda solo puede usarse una vez en una lista lambda.

Las listas lambda también se usan en otras construcciones que veremos más adelante, como la desestructuración y las macros.

Parámetros opcionales

En Common Lisp, una función puede tener algunos argumentos que son opcionales. Se designan en la lista lambda con la palabra clave de lista lambda &optional. Un parámetro se vinculará al valor nil si no se especifica. Si hay varios parámetros opcionales, se vinculan en orden. Se pueden especificar valores por defecto para los parámetros opcionales. Por último, para cada parámetro opcional se puede especificar un símbolo que se vinculará a verdadero o falso según si quien llama a la función proporcionó ese parámetro (esto se conoce como el «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))

Parámetros de palabra clave

En Common Lisp, una función puede tener parámetros con nombre (los «parámetros de palabra clave»). Se designan en la lista lambda con la palabra clave de lista lambda &key. Los parámetros de palabra clave no son parámetros obligatorios. Al igual que los parámetros opcionales, se les pueden dar valores por defecto y símbolos a los que vincular su estado de «proporcionado o no».

Al llamar a una función con parámetros de palabra clave, se usa el nombre del parámetro como palabra clave delante del valor del parámetro. Quien llama a la función puede especificar los parámetros de palabra clave en cualquier orden.

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

Hay que tener cuidado al combinar parámetros opcionales y de palabra clave, ya que los parámetros opcionales podrían consumir el nombre de palabra clave y el argumento:

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

Parámetros rest

En Common Lisp, una función puede tener un parámetro que contenga el «resto» de los argumentos después de procesar los parámetros obligatorios u opcionales. Este parámetro se designa con la palabra clave de lista lambda &rest. Si otros tipos de parámetros usan todos los argumentos de la función, el parámetro rest se vinculará a una lista vacía. Si quedan argumentos sin usar, el parámetro rest se vinculará a una lista con esos 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))

Instrucciones

Lilly, la alienígena de Lisp, ha hecho sin duda un montón de lasaña con tu ayuda. Quiere darte las gracias regalándote algunas sobras. Pero antes tiene algunos comentarios sobre una de las funciones en las que le ayudaste.

En este ejercicio vas a escribir algunas funciones más para ayudar a Lilly con la cocción de su lasaña, además de ayudarle a repartir las sobras contigo.

Tienes seis tareas: tres sobre cómo mejorar las funciones de cocción de la lasaña que ya existen y tres sobre cómo repartir las sobras.

1. Haz que preparation-time-in-minutes sea más fácil de usar

Lilly dice que usar tu preparation-time-in-minutes le resultaba un poco incómodo, ya que tenía que contar todas las capas antes de usar las funciones. Lilly se pregunta si podrías hacer una versión de la función que reciba las capas (cualquier número de ellas) como argumentos y luego calcule el tiempo de preparación en función de cuántas capas había. Como recordatorio, preparar una sola capa lleva 19 minutos.

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

2. Permite cambiar el tiempo esperado en el horno

Aunque las unidades parentales de Lilly siempre cocinaban su lasaña durante 337 minutos, Lilly admite que a otros aliens de Lisp les gusta cocinar la lasaña durante más o menos tiempo.

Así que sería bueno que remaining-minutes-in-oven no usara expected-minutes-in-oven, sino que pudiera aceptar un parámetro opcional para definir si el tiempo debe ser más largo, muy largo, más corto o muy corto. Más largo o más corto significa sumar o restar 100 al tiempo predeterminado de 337. Muy largo o muy corto significa sumar o restar 200. Si no se proporciona ningún parámetro, o el argumento es «normal», debe devolverse el valor predeterminado 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

3. Lilly recuerda otro estilo de cocción preferido

Justo después de que termines ese cambio, Lilly recuerda que hay algunos aliens de Lisp a los que les gustan los tiempos de cocción realmente cortos. En realidad, la quieren cruda. Así que ahora la función debe devolver 0 si el argumento era NIL, además de los mismos valores que en la tarea anterior.

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

4. Repartir las sobras

¡Ahora que has ayudado a mejorar las funciones de cocción de la lasaña, hay un montón de lasaña! Lilly quiere compensarte por toda tu ayuda repartiendo las sobras contigo. Tendrás que escribir una función que reciba tres parámetros: la cantidad total de sobras, el número de recipientes para sobras que Lilly encontró en sus armarios y lo mismo para ti. Como buscar recipientes para sobras puede llevar distintas cantidades de tiempo, y la cantidad exacta de sobras puede determinarse mientras tú y Lilly estáis registrando vuestros armarios, ambos estáis de acuerdo en que la función debe aceptar los argumentos en cualquier orden, de modo que puedas anotar cada número a medida que se determina. Cuando tengas los tres números, solo tienes que cerrar el paréntesis de la llamada a la función y evaluarla.

La función debe devolver la cantidad de sobras que sobran, es decir, la diferencia entre el peso de las sobras y el número total de recipientes que encontrasteis tú y 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

5. Hacer suposiciones

Después de unos cuantos viajes de ida y vuelta a vuestros respectivos armarios, ambos estáis de acuerdo en que sería genial que la función pudiera simplemente suponer que tú o Lilly teníais... digamos 10 recipientes si no se especifica.

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

6. Cantidad estándar de sobras

Mejor aún, la función podría suponer que hay suficientes sobras para llenar los recipientes. Así que si no se proporciona el peso, la función debe devolver :just-split-it. Sin embargo, si se proporciona el peso y es nil, debe 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
Editar en GitHub El enlace se abre en una ventana o pestaña nueva
Common Lisp Exercism

¿Listo para empezar Las sobras de la lasaña de Lilly?

Regístrate en Exercism para aprender y dominar Common Lisp con 27 conceptos92 ejercicios y mentoría humana real, todo gratis.