猿问

惰性I / O有什么不好?

我通常听说生产代码应避免使用惰性I / O。我的问题是,为什么?在玩弄之外使用惰性I / O可以吗?是什么使替代方法(例如枚举数)更好?



12345678_0001
浏览 341回答 3
3回答

月关宝盒

惰性IO的问题在于,释放所获取的任何资源在某种程度上都是不可预测的,因为这取决于程序如何使用数据-其“需求模式”。一旦您的程序删除了对该资源的最后一个引用,GC最终将运行并释放该资源。惰性流是一种非常方便的编程风格。这就是为什么Shell管道如此有趣和流行的原因。但是,如果资源有限(例如在高性能方案中,或预期扩展到机器极限的生产环境中),则依靠GC清理可能是不足的保证。有时您必须急于释放资源,以提高可伸缩性。那么,什么是懒惰IO的替代方案,而不是放弃增量处理(反过来又会消耗太多资源)呢?好吧,我们有foldl基于处理的(又称为迭代器或枚举器),由Oleg Kiselyov在2000年代后期引入,并且此后被许多基于网络的项目所普及。而不是像惰性流一样处理数据或成批处理,我们取而代之的是对基于块的严格处理进行抽象,并确保一旦读取了最后一个块,就最终确定了资源。这是基于Iteratee的编程的本质,并且提供了非常好的资源约束。基于iteratee的IO的缺点是它的编程模型有些笨拙(大致类似于基于事件的编程,而不是基于线程的漂亮控制)。在任何编程语言中,它绝对是一种先进的技术。对于绝大多数编程问题,惰性IO完全令人满意。但是,如果您要打开许多文件,或在许多套接字上交谈,或以其他方式同时使用许多资源,则iteratee(或枚举器)方法可能很有意义。

慕森卡

Dons提供了一个很好的答案,但是他遗漏了(对我而言)迭代对象最引人注目的功能之一:由于必须明确保留旧数据,它们使推理空间管理变得更加容易。考虑:average :: [Float] -> Floataverage xs = sum xs / length xs这是众所周知的空间泄漏,因为xs必须将整个列表保留在内存中才能计算sum和length。通过创建折页可以使有效的消费者受益:average2 :: [Float] -> Floataverage2 xs = uncurry (/) <$> foldl (\(sumT, n) x -> (sumT+x, n+1)) (0,0) xs-- N.B. this will build up thunks as written, use a strict pair and foldl'但是必须为每个流处理器执行此操作有点不方便。有一些概括(Conal Elliott-Beautiful Fold Zipping),但似乎没有流行。但是,迭代器可以使您获得类似的表达水平。aveIter = uncurry (/) <$> I.zip I.sum I.length这并不像折叠一样有效,因为该列表仍然需要重复多次,但是它是按块收集的,因此可以有效地对旧数据进行垃圾收集。为了破坏该属性,必须显式保留整个输入,例如使用stream2list:badAveIter = (\xs -> sum xs / length xs) <$> I.stream2list作为编程模型的迭代状态尚在开发中,但是比一年前要好得多。我们所学的组合子是有用的(例如zip,breakE,enumWith),并且要少一些,其结果是,内置iteratees和组合程序提供持续更表现力。就是说,唐斯(Dons)是个高级技术,这是正确的。我当然不会将它们用于每个I / O问题。

阿晨1998

最近在haskell-cafe上,Oleg Kiseljov指出unsafeInterleaveST(用于在ST monad中实现惰性IO)非常不安全-破坏了公式推理。他表明,它允许构造bad_ctx :: ((Bool,Bool) -> Bool) -> Bool 这样> bad_ctx (\(x,y) -> x == y)True> bad_ctx (\(x,y) -> y == x)False即使==是可交换的。惰性IO的另一个问题:实际的IO操作可以推迟到为时已晚,例如在关闭文件之后。从Haskell Wiki引用-惰性IO问题:例如,一个常见的初学者错误是在完成读取文件之前关闭文件:wrong = do&nbsp; &nbsp; fileData <- withFile "test.txt" ReadMode hGetContents&nbsp; &nbsp; putStr fileData问题是withFile在强制fileData之前关闭了句柄。正确的方法是将所有代码传递给withFile:right = withFile "test.txt" ReadMode $ \handle -> do&nbsp; &nbsp; fileData <- hGetContents handle&nbsp; &nbsp; putStr fileData在这里,数据在withFile完成之前被消耗掉。这通常是出乎意料的,并且容易出错。
随时随地看视频慕课网APP

相关分类

Java
我要回答