【发布时间】:2020-02-01 12:53:06
【问题描述】:
我正在尝试在 Scheme 中编写小程序,古老的游戏 Hamurabi(确切地说是诡计)。我想了解此类程序“设计”的首选方法,广泛处理用户输入。例如。使用循环、可变或不可变变量等。
我有一些我不太喜欢的工作变体。我相信我错过了一些更好的方法。以下是详细信息。 抱歉,解释太长了。
游戏本身是简单的“经济”模拟 - 我们有 3 个值,分别代表我们王国的人口、土地面积和粮食数量(也用作货币)。玩家规则数年,每年连续选择:
- 购买或出售粮食的土地数量
- 那么喂人用多少粮食
- 最后用多少粮食播种
所以我们有代表年份的迭代的外部循环。在里面我们有三个步骤。首先改变面积和纹理的数量。第二 改变粮食和人口的数量。第三是改变粮食的数量(相对于可用土地和耕种田地的人数)。第四步(无需用户输入)确定我们收集了多少新作物以及老鼠吃了什么(即增加谷物量)。
这可以通过使用全局变量和(set! ...) 表单轻松完成。但是,我想知道如何以更“功能性”的方式对其进行编码。看来我需要使用几个相互递归(尾部优化)函数来表示步骤。并且每次都将更改的值作为参数传递。 Here is gist with this approach implemented with only step of buying/selling land。它的工作原理是这样的:
You have 100 people, 700 acres of land and 9600 bushels of grain.
Land trades at 24 bushels of grain for acre
How many acres to buy? -100
You have 100 people, 600 acres of land and 12000 bushels of grain.
Land trades at 21 bushels of grain for acre
How many acres to buy? 200
这不是很方便,因为会有很多小函数,并且大多数都需要所有变量,即使有些是通过的。除了pop、area 和grain,我们还需要一些累加器(例如死于饥饿的总人数)。
所以我创建了两个函数来维护不可变的键值结构,例如
(list (cons 'pop 100) (cons 'area 1000) (cons 'grain 2800))
并将它们用作传递给每个函数的state。 prop-get 从状态中按键获取值,而 prop-set 返回修改后的副本(我怀疑库中已经实现了一些类似的结构)。
(load "props.scm")
(define (one-year state)
(map display
(list "You have "
(prop-get state 'pop) " people, "
(prop-get state 'area) " acres of land and "
(prop-get state 'grain) " bushels of grain."))
(newline)
(let ((state-upd (buy-land state)))
(step-2 state-upd)))
(define (buy-land state)
(let ((price (+ (random 10) 17))
(area (prop-get state 'area))
(grain (prop-get state 'grain)))
(map display
(list "Land trades at " price " bushels of grain for acre"))
(newline)
(display "How many acres to buy? ")
(let ((b (read)))
(prop-set (prop-set state 'area (+ area b)) 'grain (- grain (* price b))))))
Please here is the complete code in another gist.
这有点好,但完整的代码仍然有点冗长,包含所有这些 prop-gets、let 和相互递归。
这里还有哪些其他选择?我认为在可变全局变量和带有尾递归的不可变之间存在“中间”解决方案 - 比如使用命名 let 进行外部循环和一些可变结构将状态保存在局部变量中。但我觉得我可能会错过一些更简单和优雅的东西。
【问题讨论】:
标签: functional-programming architecture scheme lisp