这是一个很好的练习,可以使您更加流利地使用您一直想学习的编程语言,但是只需要稍加修改即可。这涉及使用对象,使用或模拟闭包以及扩展类型系统。
您的任务是编写代码来管理惰性列表,然后使用它来实现生成斐波那契数字的算法:
代码示例在Haskell中
let fibs = 0 : 1 : zipWith (+) fibs (tail fibs)
in take 40 fibs
结果:
[0,1,1,2,3,5,8,13,21,34,55,89,144,233,377,610,987,1597,2584,4181,6765,10946,17711,28657,46368,75025,121393,196418,317811,514229,832040,1346269,2178309,3524578,5702887,9227465,14930352,24157817,39088169,63245986]
您的惰性列表实现应符合以下准则:
- 列表节点是三件事之一:
- 无-空列表。
[]
- 缺点-一个单一项目,其余项目的列表配对:
1 : [2,3,4,5]
(:
是在Haskell利弊运营商) - Thunk-延迟计算,在需要时会生成List节点。
- 无-空列表。
- 它支持以下操作:
- nil-构造一个空列表。
- 缺点-构建一个缺点单元格。
- thunk-给定一个不带参数且返回Nil或Cons的函数,构造一个Thunk。
- 强制-给定一个List节点:
- 如果是Nil或Cons,只需将其返回即可。
- 如果是Thunk,则调用其函数以获取Nil或Cons。用该Nil或Cons替换该thunk,然后将其返回。
注意:用其强制值替换thunk是“ lazy”定义的重要组成部分。如果跳过此步骤,则上面的斐波那契算法将太慢。
- 空-查看List节点是否为Nil(强制后)。
- head(又名“ car”)-获取列表的第一项(如果为Nil,则抛出合适值)。
- tail(aka“ cdr”)-在列表的开头之后获取元素(如果为Nil,则抛出拟合)。
- zipWith-给定一个二进制函数(例如
(+)
)和两个(可能是无限个)列表,请将函数应用于列表的相应项。例:
zipWith (+) [1,2,3] [1,1,10] == [2,3,13]
- 拿-给定一个数字N和一个(可能是无限的)列表,抓住列表的前N个项目。
- 打印-打印列表中的所有项目。给定一个长列表或无限列表时,此选项应递增工作。
fibs
在自己的定义中使用自身。设置懒惰的递归有点棘手。您需要执行以下操作:- 为...分配一个钱
fibs
。现在将其保留为虚拟状态。 - 定义thunk函数,该函数取决于对的引用
fibs
。 - 使用其功能更新thunk。
您可能想通过定义一个函数来隐藏此管道,
fix
该函数使用自己的返回值调用List-returning函数。考虑打个na,这样这个想法就可以成立。- 为...分配一个钱
多态性(使用任何类型的项目列表的能力)不是必需的,但是请查看是否可以找到一种使用您的语言惯用的方法。
- 不用担心内存管理。即使是带有垃圾回收的语言,也倾向于随身携带您永远不会再使用的对象(例如,在调用堆栈上),因此,如果程序在遍历无限列表时泄漏内存,也不要感到惊讶。
请随意偏离这些准则,以适应您的语言特点,或探索替代方法。
规则:
- 选择一种您不太熟悉的语言。我不能“要求”这个,因此“荣誉系统”标记。但是,选民可以检查您的历史记录,以查看您所张贴的语言。
不要使用您语言的内置懒惰列表支持来做所有事情。发表实质性或至少有趣的内容。
Haskell即将淘汰。也就是说,除非您执行以下操作:
data List a = IORef (ListNode a) data ListNode a = Nil | Cons !a !(List a) | Thunk !(IO (ListNode a))
注意:Haskell的非严格评估不是禁止的,但是您的懒惰列表实现不应直接从那里获取其功能。实际上,看到不需要懒惰的高效,纯功能解决方案会很有趣。
蟒蛇:
- 不要使用itertools。
- 生成器很好,但是使用它们时,您必须找到某种方式来记住强制值。
zipWith (+) [1,2,3,4,5] [0,0,0] == [1,2,3]
。但是,这对于上面的Fibonacci算法并不重要,因为zipWith的两个参数都是无限列表。
zipWith
两个不同长度的列表时,行为是什么?