我似乎无法找到解决这个问题的方法。
我有这样的事情:
getFilePathForDay :: Day -> IO (Maybe FilePath)
getFilePathForDays date days = do
files <- mapM getFilePathForDay $ iterate (addDays 1) date
return . take days . catMaybes $ files
我正在尝试获取x个有效文件路径,其中x是我想要的天数,但上面的代码只是永远运行。我之前遇到过monad而遇到了这个问题,我想知道是否有一个解决方法可以解决我遇到的这个问题。
谢谢!
答案 0 :(得分:8)
代码永远运行,因为您试图在调用mapM
时对无限多个副作用计算进行排序。
由于您事先并不知道需要运行多少这些计算(如使用catMaybes
所示),因此您必须将调用交错为getFilePathForDay
剩下的计算。这可以使用unsafeInterleaveIO
来完成,但顾名思义,这不是推荐的方法。
您可以手动实现:
getFilePathForDays _ 0 = return []
getFilePathForDays date days = do
mpath <- getFilePathForDay date
case mpath of
Just path -> (path :) <$> remaining (days-1)
Nothing -> remaining days
where
remaining days = getFilePathForDays (addDays 1 date) days
可能有一种更优雅的方式,但现在还没有找到我:)
答案 1 :(得分:4)
有时懒惰的IO是正确的方法。由于你有一个无限的,懒惰的日子序列,并且你正在从该序列中懒散地采样一些(动态)范围,我们也应该只按需点击文件系统。
可以使用unsafeInterleaveIO
:
import System.IO.Unsafe ( unsafeInterleaveIO )
-- return a lazy list of filepaths, pull as many as you need
getFilePathForDays :: Day -> Int -> IO [FilePath]
getFilePathForDays date days = do
let go (d:ds) = unsafeInterleaveIO $ do
f <- getFilePathForDay d
fs <- go ds
return (f:fs)
-- an infinite, lazy stream of filepaths
fs <- go (iterate (addDays 1) date)
return . take days . catMaybes $ fs
go
返回一个懒惰的结果流,我们会尽可能多地使用它们。容易。