Tracks
/
Factor
Factor
/
Exercises
/
Cars, Assemble!
Cars, Assemble!

Cars, Assemble!

Learning Exercise

Introduction

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.

Comparison words

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

Range checks

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.

Quotations

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)

cond

When 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:

  • The pairs are tried in order. The first match wins.
  • An entry without a predicate (just a single quotation) at the end acts as the default.
  • Each predicate inspects the input but should leave the data stack the way it found it β€” dup ... <test> is the usual idiom.
  • The body of the chosen pair receives the same stack the predicate saw, so it usually starts by dropping the input and pushing the result.

Instructions

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.

1. Report whether the line is running

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"

2. Calculate the success rate

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

3. Calculate the production rate per hour

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.

4. Calculate the number of working items produced per minute

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
Edit via GitHub The link opens in a new window or tab
Factor Exercism

Ready to start Cars, Assemble!?

Sign up to Exercism to learn and master Factor with 47 concepts163 exercises, and real human mentoring, all for free.