Este exercício introduz as condicionais: escolher entre duas ou mais linhas de ação com base num valor. Baseia-se nos booleans que conheceste na Infiltração da Annalyn e na aritmética de números inteiros da Conversão de Moeda.
Todas estas palavras estão em math (e em kernel no caso do =):
= ( x y -- ? ) ! equal
< ( x y -- ? ) ! less than
<= ( x y -- ? ) ! less than or equal
> ( x y -- ? ) ! greater than
>= ( x y -- ? ) ! greater than or equal
3 3 = . ! => t
2 3 < . ! => t
3 3 <= . ! => t
3 4 = not . ! => t (inequality: combine = with not)
zero? (em math) é um atalho para o teste habitual 0 =: consome o número e coloca na pilha se ele era zero:
zero? ( n -- ? ) ! t when n is 0
between? (em math.order) testa se um valor está dentro de um intervalo inclusivo. É útil quando uma ação depende da faixa em que um número cai:
between? ( x lo hi -- ? ) ! lo <= x <= hi (inclusive)
5 1 10 between? . ! => t
0 1 10 between? . ! => f
10 1 10 between? . ! => t (inclusive at both ends)
Vais vê-lo muitas vezes como predicado de um cond (dup 1 4 between?) para escolher um ramo por intervalo em vez de por um único valor.
As palavras de comparação acima produzem booleans; para agir sobre um boolean, entregas à condicional uma ou mais quotations. Uma quotation é um fragmento de código entre parênteses retos, [ ... ]. Ao escrevê-la, colocas o código na pilha como um valor em vez de o executar: uma palavra como if decide depois qual quotation executar.
[ neg ] ! a quotation that negates the top of the stack
[ ] ! the empty quotation — does nothing
Um exercício mais adiante aborda as quotations na totalidade; por agora, lê [ ... ] como «o código a executar neste ramo».
if, when, unless
if (em kernel) recebe um boolean e duas quotations. Executa a primeira quotation quando o boolean é verdadeiro e a segunda quando é falso:
if ( ? then-quot else-quot -- )
: abs ( x -- y ) dup 0 < [ neg ] [ ] if ;
when executa a sua quotation apenas quando o boolean é verdadeiro; unless apenas quando é falso:
when ( ? quot -- )
unless ( ? quot -- )
if*, when* e unless*
Três variantes do kernel tratam o boolean como um valor que vale a pena manter quando é verdadeiro. Isto é útil quando uma palavra devolve «a coisa, ou f»:
if* ( ? true false -- ) ! truthy: true is called WITH ? on stack
when* ( ? true -- ) ! truthy: true is called WITH ? on stack
unless* ( ? false -- ) ! falsy: false runs and pushes a default
if* é a forma de dois ramos. O ramo verdadeiro é chamado com o valor ainda na pilha; o ramo falso é chamado sem ele:
42 [ ] [ "nothing" ] if* . ! prints 42
f [ ] [ "nothing" ] if* . ! prints "nothing"
unless* é a forma canónica de «valor ou predefinição». Se o valor for verdadeiro, fica como está; se for f, o valor é descartado e a quotation é executada para colocar um substituto:
"hello" [ "anonymous" ] unless* . ! => "hello"
f [ "anonymous" ] unless* . ! => "anonymous"
when* é a forma de um só ramo de if*: executa a sua quotation (com o valor ainda na pilha) apenas quando o valor é verdadeiro, e limita-se a descartar o valor quando é f. Recorre a ele para incorporar um resultado do tipo «a coisa, ou f» num valor acumulado, sem te preocupares em tratar o caso f:
0 41 [ + ] when* . ! => 41 (truthy: 41 added to the running total)
0 f [ + ] when* . ! => 0 (falsy: f dropped, total left untouched)
condQuando tens várias ações alternativas entre as quais escolher, cond (em combinators) é a escolha natural. Recebe um array de pares { predicate body } e executa o corpo do primeiro predicado que produz um valor verdadeiro:
USING: combinators ;
: classify ( n -- label )
{
{ [ dup 0 < ] [ drop "negative" ] }
{ [ dup 0 = ] [ drop "zero" ] }
[ drop "positive" ]
} cond ;
Alguns detalhes que vale a pena notar:
dup ... <test> é a expressão habitual.drop do valor de entrada e colocar o resultado.Neste exercício vais escrever código para analisar a produção de uma
linha de montagem numa fábrica de automóveis. A velocidade da linha de
montagem pode ir de 0 (desligada) a 10 (máxima).
À sua velocidade não nula mais baixa (1), são produzidos 221
automóveis por hora. A produção aumenta linearmente com a velocidade,
por isso, à velocidade 4, a linha produz 4 * 221 = 884 automóveis
por hora. No entanto, velocidades mais altas aumentam a probabilidade
de serem produzidos automóveis com defeito, que depois têm de ser
descartados.
Tens quatro tarefas. Cada uma retira da pilha um único parâmetro inteiro: a velocidade da linha de montagem.
Define production-status para devolver "stopped" quando a velocidade é 0
e "running" para qualquer outra velocidade:
0 production-status .
! => "stopped"
3 production-status .
! => "running"
Define success-rate para devolver a probabilidade de um item ser
produzido sem erro:
0: 0.0
1 a 4: 1.0
5 a 8: 0.9
9: 0.8
10: 0.77
10 success-rate .
! => 0.77
Define production-rate-per-hour para devolver a taxa de produção da
linha de montagem por hora, tendo em conta a taxa de sucesso.
Vais precisar de definir base-speed primeiro, a constante 221.
6 production-rate-per-hour .
! => 1193.4
O valor devolvido é um número de vírgula flutuante.
Define working-items-per-minute para devolver quantos automóveis
funcionais são produzidos por minuto. O resultado é um número inteiro:
os automóveis incompletos não são contados.
6 working-items-per-minute .
! => 19
Inscreve-te no Exercism para aprenderes e dominares Factor com 47 conceitos163 exercícios, e mentoria humana real, tudo grátis.