Trilhas
/
Factor
Factor
/
Exercícios
/
Estrela da lasanha
Estrela da lasanha

Estrela da lasanha

Exercício de aprendizagem

Introdução

Fazer malabarismo com a pilha funciona bem para palavras curtas. Quando uma palavra tem várias entradas e você precisa se referir a elas pelo nome, o Factor oferece locals: vínculos nomeados introduzidos com :: (para definições de palavras) ou [| | ] (para lambdas em linha).

:: ( inputs -- outputs )

Substitua : por :: e os nomes das entradas viram variáveis locais no corpo:

USING: locals ;

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

Compare com a versão que embaralha a pilha:

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

As duas são válidas. Os locals se destacam quando uma entrada é referenciada mais de uma vez ou quando a ordem das operações não bate naturalmente com a ordem da pilha.

Vinculando valores intermediários com :>

Dentro de um corpo :: (ou de uma lambda em linha [| ... |]), :> desempilha um valor da pilha de dados e o vincula a um local novo:

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

A forma imutável é a mais comum: use-a para nomear um valor intermediário, de modo que o corpo seja lido de cima para baixo:

USING: locals math ;

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

A variante com ! declara o local como mutável; mais adiante no corpo, value name! (sem dois-pontos e sem :>) o reatribui:

USING: kernel locals math math.order ;

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

Locais mutáveis se destacam dentro das palavras de iteração que você vai conhecer em exercícios posteriores (por exemplo, while).

[let: um escopo de qualquer lugar

:> só funciona dentro de um escopo lexical, que :: e [| ... |] criam automaticamente. Para introduzir locais a partir de uma palavra : simples ou no listener, envolva o código em [let ... ]:

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

Cada :> name vincula o valor do topo da pilha da expressão anterior; os vínculos ficam visíveis para o restante da forma.

USING: locals math ;

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

[let é a ponte entre definições : no estilo de pilha e vínculos nomeados: útil quando só um trecho de uma palavra se beneficia dos locais.

Lambdas: [| inputs | body ]

Dentro de uma citação, [| inputs | body ] introduz locais aos quais o corpo pode se referir pelo nome. Isso é especialmente útil com palavras de ordem superior como assoc-map, cuja citação recebe key e value:

USING: assocs locals ;

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

A mesma forma funciona para map-index (em [sequences][sequences]), cuja citação recebe o elemento e seu índice. Os nomes deixam claro qual operando desempenha qual 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 }

O efeito de pilha da lambda é determinado pela lista de entradas e pelo que o corpo dela produz.

Quando usar locals

Recorra a locals quando:

  • A mesma entrada é usada em mais de um lugar.
  • A ordem natural das operações não bate com a ordem da pilha, e o dup/swap/rot usado para corrigir isso é difícil de ler.
  • Você está dentro de uma lambda em linha passada a uma palavra de ordem superior que entrega vários valores de uma vez (como em assoc-map).

Quando o corpo é um fluxo linear limpo, as definições : simples continuam mais curtas e no mínimo igualmente legíveis.

assoc-map: uma entrada por vez

assoc-map (em assocs) mapeia uma citação sobre cada entrada de uma coleção associativa:

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

Uma lambda com locals é a forma mais limpa de escrever a citação.

cond revisitado

Quando você precisa despachar com base em f ou em um valor numérico, cond tem exatamente a forma certa: nenhum case é necessário, porque não há um único valor com o qual comparar.

Instruções

Você está de volta à cozinha, refinando a receita de lasanha do exercício Lasanha Suculenta da Leah. As tarefas aqui pedem que você lide com várias entradas ao mesmo tempo, e é exatamente aí que os locals, as vinculações nomeadas do Factor, se justificam.

1. Determine o status de cozimento

Defina cooking-status para pegar uma leitura do timer da pilha e retornar uma string de status.

  • 0 → "Lasagna is done."
  • f (o timer nunca foi definido) → "You forgot to set the timer."
  • qualquer outro 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. Estime o tempo de preparo

Defina preparation-time para receber um array de nomes de camadas e uma média de minutos por camada, e retornar o tempo total de preparo.

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

3. Calcule o macarrão e o molho necessários

Defina quantities para receber um array de nomes de camadas e retornar dois valores: quantos gramas de macarrão são necessários (50 g por camada de macarrão) e quantos litros de molho são necessários (1/5 de litro por camada de molho).

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

4. Adicione o ingrediente secreto

Alguém te manda uma lista de ingredientes; o último item é o segredo dessa pessoa. Defina add-secret-ingredient para receber a lista dela e a sua e retornar a sua lista com o segredo acrescentado.

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

5. Ajuste a escala da receita

A receita do livro rende duas porções. Defina scale-recipe para receber uma receita (um hashtable que mapeia o nome do ingrediente para a quantidade) e um número desejado de porções, e retornar uma nova receita ajustada na proporção certa.

H{ { "noodles" 200 } { "sauce" 1/2 } { "mozzarella" 1 } { "meat" 100 } }
4 scale-recipe .
! => H{ { "noodles" 400 } { "sauce" 1 } { "mozzarella" 2 } { "meat" 200 } }
Editar via GitHub O link abre em uma nova janela ou aba
Factor Exercism

Tudo pronto para começar Estrela da lasanha?

Crie sua conta no Exercism para aprender e dominar Factor com 47 conceitos163 exercícios e mentoria humana de verdade, tudo de graça.