轨道
/
Factor
Factor
/
练习
/
汽车,组装!
汽车,组装!

汽车,组装!

学习练习

简介

本练习介绍条件分支:根据一个值,在 2 种或更多做法之间做出选择。它建立在你于安娜琳的渗透中见过的布尔值,以及货币换算中的整数运算之上。

比较词

它们都定义在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中)接受一个布尔值和 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辆汽车。不过,速度越高,生产出故障汽车的可能性就越大,而这些汽车随后必须被丢弃。

你有四个任务。每个任务都从栈上取一个整数形参,也就是装配线的速度。

1. 报告装配线是否正在运行

定义production-status:当速度为0时返回"stopped",其他任何速度返回"running":

0 production-status .
! => "stopped"
3 production-status .
! => "running"

2. 计算成功率

定义success-rate,返回一件产品被无差错生产出来的概率:

  • 0:0.0
  • 1到4:1.0
  • 5到8:0.9
  • 9:0.8
  • 10:0.77
10 success-rate .
! => 0.77

3. 计算每小时的产量

定义production-rate-per-hour,返回装配线每小时的产量,并考虑成功率。

你需要先定义base-speed,也就是常量221。

6 production-rate-per-hour .
! => 1193.4

返回的值是浮点数。

4. 计算每分钟生产的合格品数量

定义working-items-per-minute,返回每分钟生产多少辆合格汽车。结果是整数,不完整的汽车不计入。

6 working-items-per-minute .
! => 19
通过 GitHub 编辑 链接将在新窗口或新标签页中打开
Factor Exercism

准备好开始 汽车,组装! 了吗?

注册 Exercism,借助 47 个概念163 个练习 和真人导师指导,学习并掌握 Factor,全部免费。