为什么iterate
不是这样定义的
iterate :: (a -> a) -> a -> [a]
iterate f x = xs where xs = x : map f xs
在前奏中?
像这样喜结连理似乎不会增加分享。
对比:
cycle xs = let x = xs ++ x in x
在这里打结具有在内存中创建循环链表的效果。 x
是它自己的尾巴。 这是一个真正的收获。
您建议的实现不会增加对朴素实现的共享。而且它首先没有办法这样做 - 无论如何,像iterate (+1) 0
这样的东西没有共享结构。
您的版本中没有打结,它只是在生成的列表中保留一个档次的指针,以找到下一次迭代的输入值。这意味着在生成下一个单元格之前,无法对每个列表单元格进行 gc-ed。
相比之下,Prelude的版本使用iterate
的调用帧,并且由于只需要一次,一个好的编译器可以重用该帧并改变其中的值,以实现更优化的整体操作(在这种情况下,列表的单元格彼此独立)。
为了清楚起见,我在下面包括了Prelude定义,它不需要调用map的开销。
iterate f x = x : iterate f (f x)
只是为了好玩,我做了一个小的快速程序来测试你的iterate
与前奏 - 只是为了简化到正常形式take 100000000 $ iterate (+1) 0
(这是一个Int
的列表)。我只运行了 5 次测试,但您的版本运行了 7.833
(最多 7.873
分钟 7.667
),而 Prelude 的版本在 7.519
(最多 7.591
分钟 7.477
)。我怀疑时差是map
被召唤的开销。
第二个原因很简单:可读性。