本练习介绍条件分支:根据一个值,在 2 种或更多做法之间做出选择。它建立在你于安娜琳的渗透中见过的布尔值,以及货币换算中的整数运算之上。
= ( 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中)接受一个布尔值和 2 个引用块。当布尔值为真时,它运行第一个引用块;为假时运行第二个:
if ( ? then-quot else-quot -- )
: abs ( x -- y ) dup 0 < [ neg ] [ ] if ;
when 只在布尔值为真时运行它的引用块;unless 只在为假时运行:
when ( ? quot -- )
unless ( ? quot -- )
if*、when* 和 unless*
kernel 里有 3 种变体,它们在布尔值为真时,把这个布尔值当作一个值得保留的值;当某个词要返回“那个东西,或者 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:当速度为0时返回"stopped",其他任何速度返回"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