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.
:>
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.
[| 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.
Recorra a locals quando:
dup/swap/rot usado para corrigir isso é difícil de ler.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 vezassoc-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 revisitadoQuando 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.
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.
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."
"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."
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
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
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" }
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 } }
Crie sua conta no Exercism para aprender e dominar Factor com 47 conceitos163 exercícios e mentoria humana de verdade, tudo de graça.