And*_*rey 4 file-io haskell lazy-loading lazy-evaluation lazy-sequences
我有一个由find创建的懒惰文件名列表.我希望能够懒得加载这些文件的元数据.这意味着,如果i 元素来自,它应该只搜索这十个文件的元数据.如果您在没有挂起磁盘的情况下请求它们,那么事实是完美地为您提供了10个文件,而我的脚本搜索所有文件的元数据.take 10
metadata
find
main = do
files <- find always always /
metadata <- loadMetaList files
loadMetaList :: [String] -> IO [Metadata]
loadMetaList file:files = do
first <- loadMeta file
rest <- loadMetaList files
return (first:rest)
loadMeta :: String -> IO Metadata
Run Code Online (Sandbox Code Playgroud)
如您所见,loadMetaList 不是懒惰的.因为它是懒惰的,它应该使用尾递归.有点像return (first:loadMetaList rest)
.
如何使loadMetaList变得懒惰?
在(>>=)
该的IO
单子是这样的,在
loadMetaList :: [String] -> IO [Metadata]
loadMetaList file:files = do
first <- loadMeta file
rest <- loadMetaList files
return (first:rest)
Run Code Online (Sandbox Code Playgroud)
loadMetaList files
必须在执行之前运行该操作return (first:rest)
.
您可以通过推迟执行来避免这种情况loadMetaList files
,
import System.IO.Unsafe
loadMetaList :: [String] -> IO [Metadata]
loadMetaList file:files = do
first <- loadMeta file
rest <- unsafeInterleaveIO $ loadMetaList files
return (first:rest)
Run Code Online (Sandbox Code Playgroud)
与unsafeInterleaveIO
(find
也使用).这样,loadMetaList files
直到需要它才会执行,如果只需要10个文件的元数据,那么只会加载它.
它并不像它的堂兄那样不安全unsafePerformIO
,但也应该小心处理.
这是你如何做到这pipes
一点.我真的不知道你是怎么实现loadMeta
和find
,所以我做的东西了:
import Pipes
find :: Producer FilePath IO ()
find = each ["heavy.mp3", "metal.mp3"]
type MetaData = String
loadMeta :: String -> IO MetaData
loadMeta file = return $ "This song is " ++ takeWhile (/= '.') file
loadMetaList :: Pipe FilePath MetaData IO r
loadMetaList = mapM loadMeta
Run Code Online (Sandbox Code Playgroud)
要运行它,我们只需构建像管道一样的处理阶段并使用runEffect
以下命令运行管道:
>>> runEffect $ find >-> loadMetaList >-> stdoutLn
This song is heavy
This song is metal
Run Code Online (Sandbox Code Playgroud)
有几点需要指出:
你可以制作find
一个Producer
它只是懒惰地搜索目录树.我知道你不需要这个功能,因为你的文件集现在很小,但是当你的目录变大时很容易包含.
它很懒,但没有unsafeInterleaveIO
.它立即生成每个输出,并且不等待首先收集整个结果列表.
例如,即使我们使用无限的文件列表,它也会起作用:
>>> import qualified Pipes.Prelude as Pipes
>>> runEffect $ each (cycle ["heavy.mp3", "metal.mp3"]) >-> loadMetaList >-> Pipes.stdoutLn
This song is heavy
This song is metal
This song is heavy
This song is metal
This song is heavy
This song is metal
...
Run Code Online (Sandbox Code Playgroud)
例如,我们可以使用take
以下内容限制结果数量:
>>> runEffect $ each (cycle ["heavy.mp3", "metal.mp3"]) >-> loadMetaList >-> Pipes.take 3 >-> Pipes.stdoutLn
This song is heavy
This song is metal
This song is heavy
Run Code Online (Sandbox Code Playgroud)
所以你问到了什么问题unsafeInterleaveIO
.主要限制unsafeInterleaveIO
是您无法保证IO
实际发生的行为何时会导致以下常见陷阱:
Handle
在读取文件之前意外关闭了
IO
行动迟到或永远不会发生
纯代码有副作用和投掷IOException
s
Haskell IO
系统相对于其他语言的最大优点是Haskell完全将评估模型与副作用的顺序分离.当你使用懒惰时IO
,你会失去那种脱钩,然后副作用的顺序变得与Haskell的评估模型紧密结合,这是一个巨大的倒退.
这就是为什么使用懒惰一般都不明智IO
,特别是现在有了简单而优雅的替代方案.
如果您想了解更多关于如何安全地pipes
实现懒惰的信息IO
,那么您可以阅读大量的管道教程.
归档时间: |
|
查看次数: |
396 次 |
最近记录: |