为什么不从前奏曲中“迭代”喜结良缘?
Why doesn't `iterate` from the Prelude tie the knot?
为什么 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
之类的东西都没有共享结构。
为清楚起见,我在下面包含的 Prelude 定义不需要调用映射的开销。
iterate f x = x : iterate f (f x)
只是为了好玩,我制作了一个小程序来测试你的 iterate
与 Prelude 的对比 - 只是为了减少到正常形式 take 100000000 $ iterate (+1) 0
(这是 Int
的列表).我只 运行 5 次测试,但你的版本 运行 为 7.833
(最大 7.873
最小 7.667
)而 Prelude 为 7.519
(最大7.591
分钟 7.477
)。我怀疑时差是 map
被调用的开销。
第二个原因很简单:可读性。
您的版本中没有打结,它只是在生成的列表上保留一个指针,以便在下一次迭代中找到输入值。这意味着在生成下一个单元格之前,不能对每个列表单元格进行 gc-ed。
相比之下,Prelude 的版本为此使用了 iterate
的调用框架,并且由于它只需要一次,所以一个好的编译器可以重用那个框架并改变其中的值,以实现更优化的操作总体而言(在这种情况下,列表的单元格彼此独立)。
为什么 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
之类的东西都没有共享结构。
为清楚起见,我在下面包含的 Prelude 定义不需要调用映射的开销。
iterate f x = x : iterate f (f x)
只是为了好玩,我制作了一个小程序来测试你的 iterate
与 Prelude 的对比 - 只是为了减少到正常形式 take 100000000 $ iterate (+1) 0
(这是 Int
的列表).我只 运行 5 次测试,但你的版本 运行 为 7.833
(最大 7.873
最小 7.667
)而 Prelude 为 7.519
(最大7.591
分钟 7.477
)。我怀疑时差是 map
被调用的开销。
第二个原因很简单:可读性。
您的版本中没有打结,它只是在生成的列表上保留一个指针,以便在下一次迭代中找到输入值。这意味着在生成下一个单元格之前,不能对每个列表单元格进行 gc-ed。
相比之下,Prelude 的版本为此使用了 iterate
的调用框架,并且由于它只需要一次,所以一个好的编译器可以重用那个框架并改变其中的值,以实现更优化的操作总体而言(在这种情况下,列表的单元格彼此独立)。