嘲笑jbapple关于的出色答案replicate
,但改为使用replicateA
(replicate
基于)构建,我想到了以下内容:
--Unlike fromList, one needs the length explicitly.
myFromList :: Int -> [b] -> Seq b
myFromList l xs = flip evalState xs $ Seq.replicateA l go
where go = do
(y:ys) <- get
put ys
return y
myFromList
(在效率稍高的版本中)(已在内部定义)并在内部Data.Sequence
用于构建作为排序结果的手指树。
通常,直觉replicateA
很简单。replicateA
在applicativeTree函数的顶部构建。applicativeTree
取片的大小的树m
,并生成包含一个均衡树n
的这个副本。对于箱子n
多达8(单个Deep
手指)被硬编码。在此之上的任何东西,它都会递归调用。“应用”元素只是简单地它通过诸如状态(例如,在上述代码的情况下)之类的线程效果交错树的构造。
go
复制的函数只是一个操作,该操作获取当前状态,将元素弹出顶部,然后替换其余部分。因此,在每次调用时,它会进一步降低作为输入提供的列表。
一些更具体的笔记
main = print (length (show (Seq.fromList [1..10000000::Int])))
在一些简单的测试中,这产生了有趣的性能折衷。myFromList的上述主要功能比的低了近1/3 fromList
。另一方面,myFromList
使用了2MB的恒定堆,而标准fromList
使用了926MB。926MB是由于需要立即将整个列表保存在内存中而产生的。同时,该解决方案myFromList
能够以惰性流方式消耗结构。速度问题是由于myFromList
必须执行大约两倍的分配(由于状态monad的成对构造/销毁)导致的,fromList
。我们可以通过转移到CPS转换后的状态monad来消除这些分配,但这会导致在任何给定时间保留更多的内存,因为懒惰的损失要求以非流方式遍历列表。
另一方面,如果我myFromList
不想强迫整个演出进行表演,而是移至仅提取head或last元素,立即带来更大的胜利-提取head元素几乎是即时的,而提取last元素为0.8s 。同时,使用standard fromList
,提取head或last元素花费约2.3秒。
这是所有细节,是纯洁和懒惰的结果。在发生突变和随机访问的情况下,我想该replicate
解决方案绝对更好。
但是,这确实提出了一个问题,即是否存在一种重写的方法,applicativeTree
这种myFromList
方法严格效率更高。我认为问题在于,应用操作的执行顺序与自然遍历树的顺序不同,但是我还没有完全研究其工作方式,或者是否有解决此问题的方法。