Tracks
/
Factor
Factor
/
Ejercicios
/
Lumbrera de la lasaña
Lumbrera de la lasaña

Lumbrera de la lasaña

Ejercicio de aprendizaje

Introducción

Hacer malabares con la pila está bien para palabras cortas. Cuando una palabra tiene varios argumentos y necesitas referirte a ellos por su nombre, Factor ofrece locals: enlaces con nombre que se introducen con :: (para definiciones de palabras) o [| | ] (para lambdas en línea).

:: ( inputs -- outputs )

Reemplaza : con :: y los nombres de los argumentos se convierten en variables locales dentro del cuerpo:

USING: locals ;

:: hypotenuse ( a b -- c )
    a a *
    b b *
    + sqrt ;

Compáralo con la versión que reordena la pila:

! DOCTEST: SKIP   (shown for comparison — would re-define hypotenuse)
: hypotenuse ( a b -- c )
    [ sq ] bi@ + sqrt ;

Ambas son válidas. Los locals brillan cuando se hace referencia a un argumento más de una vez o cuando el orden de las operaciones no coincide de forma natural con el orden de la pila.

Enlazar valores intermedios con :>

Dentro del cuerpo de un :: (o de una lambda en línea [| ... |]), :> saca un valor de la pila de datos y lo enlaza a un local nuevo:

value :> name      ! immutable binding
value :> name!     ! mutable binding (name! is its setter)

La forma inmutable es la habitual. Úsala para nombrar un valor intermedio y que el cuerpo se lea de arriba hacia abajo:

USING: locals math ;

:: average-3 ( x y z -- avg )
    x y + z + :> sum
    sum 3 / ;

La variante con ! declara el local como mutable; más adelante en el cuerpo, value name! (sin dos puntos y sin :>) lo reasigna:

USING: kernel locals math math.order ;

:: clamped ( x lo hi -- y )
    x :> v!
    v lo < [ lo v! ] when
    v hi > [ hi v! ] when
    v ;

Los locals mutables brillan dentro de las palabras de iteración que conocerás en ejercicios posteriores (por ejemplo, while).

[let: un ámbito desde cualquier lugar

:> solo funciona dentro de un ámbito léxico, que :: y [| ... |] crean automáticamente. Para introducir locals desde una palabra : normal o desde el listener, envuelve el código en [let ... ]:

[let code :> name code :> name ... body ]

Cada :> name enlaza el valor en el tope de la pila de la expresión anterior; los enlaces son visibles para el resto de la forma.

USING: locals math ;

[let 3 :> x 4 :> y x x * y y * + ] .   ! => 25

[let es el puente entre las definiciones : al estilo de pila y los enlaces con nombre; resulta útil cuando solo una parte de una palabra se beneficia de los locals.

Lambdas: [| inputs | body ]

Dentro de una quotation, [| inputs | body ] introduce locals a los que el cuerpo puede referirse por su nombre. Esto es especialmente útil con palabras de orden superior como assoc-map, cuya quotation recibe key y value:

USING: assocs locals ;

H{ { "a" 1 } { "b" 2 } }
[| key value | key value neg ] assoc-map .
! => H{ { "a" -1 } { "b" -2 } }

La misma forma funciona para map-index (en [sequences][sequences]), cuya quotation recibe el elemento y su índice. Los nombres dejan claro qué operando desempeña cada papel:

USING: locals sequences ;

! Divide each element by its 1-based position:
{ 10 80 270 } [| elt i | elt i 1 + / ] map-index .
! => { 10 40 90 }

El efecto de pila de la lambda lo determinan la lista de argumentos y lo que produce su cuerpo.

Cuándo usar locals

Recurre a los locals cuando:

  • Se use el mismo argumento en más de un lugar.
  • El orden natural de las operaciones no coincida con el orden de la pila, y el dup/swap/rot que lo corrige sea difícil de leer.
  • Estés dentro de una lambda en línea que se pasa a una palabra de orden superior que te entrega varios valores a la vez (como en assoc-map).

Cuando el cuerpo es un flujo lineal y limpio, las definiciones : simples siguen siendo más cortas y al menos igual de legibles.

assoc-map: una entrada a la vez

assoc-map (en assocs) aplica una quotation a cada entrada de una colección asociativa:

assoc-map ( assoc quot: ( key value -- newkey newvalue ) -- newassoc )

Una lambda con locals es la forma más limpia de escribir la quotation.

Volvamos a cond

Cuando necesitas elegir según f o según un valor numérico, cond tiene exactamente la forma correcta: no hace falta case, porque no hay un único valor con el que comparar.

Instrucciones

Estás de vuelta en la cocina, mejorando la receta de lasaña de La deliciosa lasaña de Leah. Las tareas de este ejercicio te piden manejar varios argumentos a la vez, y es justo ahí cuando los locals, los enlaces con nombre de Factor, demuestran para qué sirven.

1. Determina el estado de cocción

Define cooking-status para que tome de la pila una lectura del temporizador y devuelva un string con el estado.

  • 0 → "Lasagna is done."
  • f (el temporizador nunca se puso) → "You forgot to set the timer."
  • cualquier otro número → "Not done, please wait."
12 cooking-status .   ! => "Not done, please wait."
0 cooking-status .    ! => "Lasagna is done."
f cooking-status .    ! => "You forgot to set the timer."

2. Estima el tiempo de preparación

Define preparation-time para que tome un array con los nombres de las capas y un promedio de minutos por capa, y devuelva el tiempo total de preparación.

{ "sauce" "noodles" "sauce" "meat" "mozzarella" "noodles" } 3 preparation-time .
! => 18

3. Calcula los fideos y la salsa necesarios

Define quantities para que tome un array con los nombres de las capas y devuelva dos valores: los gramos de fideos necesarios (50 g por cada capa de fideos) y los litros de salsa necesarios (1/5 de litro por cada capa de salsa).

{ "sauce" "noodles" "sauce" "meat" "mozzarella" "noodles" } quantities .s
! => 100
! => 2/5

4. Agrega el ingrediente secreto

Tu amigo te manda una lista de ingredientes; el último elemento es su secreto. Define add-secret-ingredient para que tome su lista y la tuya, y devuelva tu lista con el secreto agregado al final.

{ "noodles" "sauce" "mozzarella" "kampot pepper" }
{ "noodles" "meat" "sauce" "mozzarella" }
add-secret-ingredient .
! => { "noodles" "meat" "sauce" "mozzarella" "kampot pepper" }

5. Escala la receta

La receta del libro rinde dos porciones. Define scale-recipe para que tome una receta (un hashtable que va del nombre de cada ingrediente a su cantidad) y un número de porciones objetivo, y devuelva una receta nueva escalada de forma adecuada.

H{ { "noodles" 200 } { "sauce" 1/2 } { "mozzarella" 1 } { "meat" 100 } }
4 scale-recipe .
! => H{ { "noodles" 400 } { "sauce" 1 } { "mozzarella" 2 } { "meat" 200 } }
Editar en GitHub El enlace se abre en una ventana o una pestaña nuevas
Factor Exercism

¿Todo listo para empezar Lumbrera de la lasaña?

Regístrate en Exercism para aprender y dominar Factor con 47 conceptos163 ejercicios y mentoría humana real, todo gratis.