项目 3:搭档 A 进阶练习

Project 3 中搭档 A 的第二部分练习(A6–A8):警察与小偷、金钱系统和餐馆。

adv.scm 里有 thief 类的定义,它是 person 的子类。thief 是一种试图从别人那里偷食物的角色。当然,伯克利 campus 不会容忍这种行为太久。你的任务是定义一个 police 类;police 对象会抓住 thief,并把他们直接送进 jail。要做到这一点,你需要先理解小偷的工作方式。

由于 thief 是一种 person,每当另一个 person 进入 thief 所在的地方,thief 就会从地方那里收到一条 notice 消息。thief 注意到有新的人时,会根据内部 behavior 变量的状态做两件事之一。如果这个变量设成 steal,小偷就环顾四周,看看地方里有没有食物。如果有,小偷就从当前持有者手中拿走食物,并把自己的 behavior 设为 run。当小偷的 behavior 是 run 时,他只要 notice 到有人进入他当前所在地点,就会随机移动到一个新地方。run 行为让小偷很难被抓到。

注意,thief 对象把很多消息委托给它的 person 对象。

第 A6 题:第一部分

为了帮警察开展工作,你需要创建一个叫 jail 的地点(即 jail 是 place 的一个实例)。监狱没有出口。此外,你还需要为人物和小偷创建一个叫 go-directly-to 的方法。go-directly-to 不要求 new-place 与 current-place 相邻。所以通过调用 (ask thief 'go-directly-to jail),警察就能把小偷送进监狱,无论小偷此刻身在何处(假设变量 thief 绑定着被抓的小偷)。

第 A6 题:第二部分

小偷有时会尝试朝一个随机选择的方向离开所在地点。事实证明,如果那个地方没有出口,这就行不通——比如 jail。请修改 thief 类,让小偷不会尝试离开一个没有出口的地方。

合并工作

继续之前,请让搭档讲解第 B6 题及其解答。同时,也向你的搭档讲解第 A6 题及其解答。

第 A7 题:第一部分

现在我们要发明餐馆对象。人们将通过在那里买食物与餐馆互动。首先,我们必须让买东西成为可能。请给 person 对象一个 money 属性,它是一个数字,表示他们有多少美元。注意 money 不是一个对象。我们把它实现为一个数字,因为与椅子、锅贴这类对象不同,一个人需要能花掉一部分钱而不必全部花光。原则上我们可以发明 quarter(25 美分)和 dollar-bill(一美元纸币)这样的对象,但这会让找零过程变得复杂,毫无必要。

为简单起见,我们让每个 person 一开始都有 $100。(按理说我们应当让人身无分文,再发明银行和工作等等,但我们不这么做。)请为人物创建两个方法:get-money 和 pay-money,各接受一个数字作为参数,并相应地更新人物的 money 值。pay-money 必须根据人物钱够不够返回 true 或 false。

> (ask brian 'money)
100
> (ask brian 'get-money 20) ;增加金钱
> (ask brian 'money)
120
> (ask brian 'pay-money 30) ;减少金钱。如果钱够则返回 #t  
#t  
> (ask brian 'money)
90

第 A7 题:第二部分

冒险游戏的另一个问题是:Noah’s 只有一个贝果。一旦有人拿走了那个贝果,他们就断粮了。其他餐馆也一样。

为了解决这个问题,我们要发明一种新的地点,叫做 restaurant。(也就是说,restaurant 是 place 的子类。)每家 restaurant 只卖一种食物。(这当然是简化;不难看出,我们可以扩展项目,允许食物种类列表。)restaurant 被实例化时,除了所有地点都有的参数外,还应有两个额外参数:这家餐馆卖的食物对象的类,以及一份这种食物的价格:

   > (define-class (pasta) (parent (food ...)) ...)
 
   > (define somerestaurant (instantiate restaurant 'somerestaurant pasta 7))

注意,传给餐馆的参数是一个类,不是某个具体的贝果(实例)。下面是 Pasta 食物类的示例。你的搭档本应在第 B6 题中定义过一些食物类的例子。

> (define pesto-pasta (instantiate pasta))
> (ask pesto-pasta 'calories)
150

餐馆应当有两个方法。menu 方法返回一个列表,包含餐馆所卖食物的名字和价格。sell 方法接受两个参数:想买东西的 person,以及他想要的食物的名字。sell 方法必须先检查餐馆确实卖这种食物。如果是,就 ask 买家 pay-money 相应的金额。如果成功,方法就实例化该食物类并返回新的食物对象。如果买家买不起,方法应返回 #f。

以下是一些例子:

> (ask somerestaurant 'menu)
(pasta 7)
> (ask somerestaurant 'sell someperson 'pasta) ;注意 pasta 是名字

第 A8 题

现在我们需要给人物的 buy 方法。它应当接受想买的食物的名字作为参数:

> (ask Brian 'buy 'bagel)

这个方法必须向餐馆发一条 sell 消息。如果成功(也就是说,sell 方法返回的值是一个对象而不是 #f),新食物就应加进人物的 possessions。如果人物买不起,返回一个错误。