This exercise introduces conditionals β choosing between two or more courses of action based on a value. It builds on the booleans you met in Annalyn's Infiltration and the integer arithmetic from Currency Conversion.
These all live in math (and kernel for =):
= ( 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? (in math) is a shorthand for the common 0 = test β
it consumes the number and pushes whether it was zero:
zero? ( n -- ? ) ! t when n is 0
between? (in math.order) tests whether a value
falls within an inclusive range. It's handy when an action depends
on which band a number lands in:
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)
You'll often see it as a cond predicate β dup 1 4 between? β
to pick a branch by range rather than by a single value.
The comparison words above produce booleans; to act on a boolean you
hand the conditional one or more quotations. A quotation is a
snippet of code wrapped in square brackets, [ ... ]. Writing it
pushes the code onto the stack as a value instead of running it β a
word like if then decides which quotation to run.
[ neg ] ! a quotation that negates the top of the stack
[ ] ! the empty quotation β does nothing
A later exercise covers quotations in full; for now, read
[ ... ] as "the code to run for this branch."
if, when, unless
if (in kernel) takes a boolean and two quotations. It
runs the first quotation when the boolean is truthy and the second
when it is falsy:
if ( ? then-quot else-quot -- )
: abs ( x -- y ) dup 0 < [ neg ] [ ] if ;
when runs its quotation only when the boolean is truthy; unless
only when it is falsy:
when ( ? quot -- )
unless ( ? quot -- )
if*, when*, and unless*
Three kernel variants treat the boolean as a value worth keeping
when it's truthy β useful when a word returns "the thing, or 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* is the two-branch form. The truthy branch is called with
the value still on the stack; the falsy branch is called
without it:
42 [ ] [ "nothing" ] if* . ! prints 42
f [ ] [ "nothing" ] if* . ! prints "nothing"
unless* is the canonical "value or default" idiom. If the value
is truthy, it's left alone; if it's f, the value is dropped and
the quotation runs to push a substitute:
"hello" [ "anonymous" ] unless* . ! => "hello"
f [ "anonymous" ] unless* . ! => "anonymous"
when* is the one-branch form of if*: it runs its quotation β
with the value still on the stack β only when the value is truthy,
and simply drops the value when it's f. Reach for it to fold a
"the thing, or f" result into a running value without bothering
to handle the f case:
0 41 [ + ] when* . ! => 41 (truthy: 41 added to the running total)
0 f [ + ] when* . ! => 0 (falsy: f dropped, total left untouched)
condWhen you have several alternative actions to choose between, cond
(in combinators) is the natural fit. It takes an
array of { predicate body } pairs and runs the body of the first
predicate that yields a truthy value:
USING: combinators ;
: classify ( n -- label )
{
{ [ dup 0 < ] [ drop "negative" ] }
{ [ dup 0 = ] [ drop "zero" ] }
[ drop "positive" ]
} cond ;
A few details worth noting:
dup ... <test> is the usual idiom.dropping the input and pushing the
result.In this exercise you'll be writing code to analyze the production of
an assembly line in a car factory. The assembly line's speed can range
from 0 (off) to 10 (maximum).
At its lowest non-zero speed (1), 221 cars are produced each hour.
The production increases linearly with the speed, so at speed 4 the
line produces 4 * 221 = 884 cars per hour. However, higher speeds
increase the likelihood that faulty cars are produced, which then
have to be discarded.
You have four tasks. Each takes a single integer parameter β the speed of the assembly line β off the stack.
Define production-status to return "stopped" when the speed is 0
and "running" for any other speed:
0 production-status .
! => "stopped"
3 production-status .
! => "running"
Define success-rate to return the probability of an item being
produced without error:
0: 0.0
1 to 4: 1.0
5 to 8: 0.9
9: 0.8
10: 0.77
10 success-rate .
! => 0.77
Define production-rate-per-hour to return the assembly line's
production rate per hour, taking the success rate into account.
You'll need to define base-speed first, the constant 221.
6 production-rate-per-hour .
! => 1193.4
The value returned is floating-point.
Define working-items-per-minute to return how many working cars are
produced per minute. The result is an integer β partial cars are not
counted.
6 working-items-per-minute .
! => 19
Sign up to Exercism to learn and master Factor with 47 concepts163 exercises, and real human mentoring, all for free.