Ця вправа знайомить з умовними конструкціями, тобто з вибором між двома або більше варіантами дій залежно від значення. Вона спирається на булеві значення (англ. Boolean), з якими ми познайомилися у вправі Інфільтрація Анналін, та на арифметику цілих чисел зі вправи Конвертація валют.
Усі вони містяться в math (а = - у kernel):
= ( 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? (у math) - це скорочення для поширеної перевірки 0 =: він споживає число і кладе на стек те, чи було воно нулем:
zero? ( n -- ? ) ! t when n is 0
between? (у math.order) перевіряє, чи лежить значення в діапазоні разом з його межами. Він стає в пригоді, коли дія залежить від того, у який діапазон потрапляє число:
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)
Ми часто бачимо його як предикат cond (dup 1 4 between?), щоб вибрати гілку за діапазоном, а не за одним значенням.
Слова порівняння вище дають булеві значення. Щоб діяти за булевим значенням, ми передаємо умовній конструкції одну або кілька квотацій. Квотація - це фрагмент коду в квадратних дужках, [ ... ]. Коли ми її пишемо, код потрапляє на стек як значення, а не виконується, а слово на кшталт if потім вирішує, яку квотацію запустити.
[ neg ] ! a quotation that negates the top of the stack
[ ] ! the empty quotation — does nothing
Пізніша вправа розповість про квотації докладніше. А поки що читаймо [ ... ] як «код, який треба виконати для цієї гілки».
if, when, unless
if (у kernel) приймає булеве значення і дві квотації. Він виконує першу квотацію, коли булеве значення правдиве, і другу, коли воно неправдиве:
if ( ? then-quot else-quot -- )
: abs ( x -- y ) dup 0 < [ neg ] [ ] if ;
when виконує свою квотацію, лише коли булеве значення правдиве, а unless - лише коли воно неправдиве:
when ( ? quot -- )
unless ( ? quot -- )
if*, when* і unless*
Три варіанти з kernel ставляться до булевого значення як до значення, яке варто зберегти, коли воно правдиве. Це стає в пригоді, коли слово повертає «річ або 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* - це форма з двома гілками. Правдиву гілку викликають зі значенням, що залишається на стеку, а неправдиву - без нього:
42 [ ] [ "nothing" ] if* . ! prints 42
f [ ] [ "nothing" ] if* . ! prints "nothing"
unless* - це класичний прийом «значення або типове значення». Якщо значення правдиве, його залишають як є. Якщо воно f, значення відкидають, і запускається квотація, яка кладе на стек замінник:
"hello" [ "anonymous" ] unless* . ! => "hello"
f [ "anonymous" ] unless* . ! => "anonymous"
when* - це форма if* з однією гілкою: він виконує свою квотацію (зі значенням, що все ще на стеку) лише тоді, коли значення правдиве, і просто відкидає його, коли воно f. Він стане в пригоді, коли треба вкласти результат «річ або f» у поточне значення, не турбуючись про випадок f:
0 41 [ + ] when* . ! => 41 (truthy: 41 added to the running total)
0 f [ + ] when* . ! => 0 (falsy: f dropped, total left untouched)
condКоли треба вибрати одну з кількох альтернативних дій, cond (у combinators) підходить найкраще. Він приймає масив пар { predicate body } і виконує тіло першого предиката, який дає правдиве значення:
USING: combinators ;
: classify ( n -- label )
{
{ [ dup 0 < ] [ drop "negative" ] }
{ [ dup 0 = ] [ drop "zero" ] }
[ drop "positive" ]
} cond ;
Кілька деталей, на які варто звернути увагу:
dup ... <test>.drop і кладе на стек результат.У цій вправі ми будемо писати код, щоб аналізувати виробництво на складальній лінії автомобільного заводу. Швидкість складальної лінії може бути в межах від 0 (вимкнено) до 10 (максимум).
За найнижчої ненульової швидкості (1) щогодини виробляється 221 автомобіль. Виробництво зростає лінійно зі швидкістю, тож за швидкості 4 лінія виробляє 4 * 221 = 884 автомобілі на годину. Однак вищі швидкості підвищують ймовірність виробництва бракованих автомобілів, які потім доводиться відкидати.
У нас є чотири завдання. Кожне з них бере зі стеку єдиний цілочисельний параметр: швидкість складальної лінії.
Визначте production-status так, щоб вона повертала "stopped", коли швидкість дорівнює 0, і "running" для будь-якої іншої швидкості:
0 production-status .
! => "stopped"
3 production-status .
! => "running"
Визначте success-rate так, щоб вона повертала ймовірність того, що виріб виготовлено без помилок:
0: 0.0
1 до 4: 1.0
5 до 8: 0.9
9: 0.8
10: 0.77
10 success-rate .
! => 0.77
Визначте production-rate-per-hour так, щоб вона повертала швидкість виробництва складальної лінії за годину з урахуванням коефіцієнта успішності.
Спочатку нам потрібно визначити base-speed як константу 221.
6 production-rate-per-hour .
! => 1193.4
Повернене значення має тип числа з плаваючою комою.
Визначте working-items-per-minute так, щоб вона повертала кількість придатних автомобілів, виготовлених за хвилину. Результат має бути цілим числом; неповні автомобілі не враховуються.
6 working-items-per-minute .
! => 19
Зареєструйтеся на Exercism, щоб вивчати й опановувати Factor, а також 47 концепцій163 вправи та справжнє наставництво від людей, і все це безкоштовно.