[英]Lazily evaluate monadic functions in Haskell
我似乎無法弄清楚我遇到的這個問題的解決方法。
我有這樣的事情:
getFilePathForDay :: Day -> IO (Maybe FilePath)
getFilePathForDays date days = do
files <- mapM getFilePathForDay $ iterate (addDays 1) date
return . take days . catMaybes $ files
我試圖得到x個有效的文件路徑,其中x是我想要的天數,但上面的代碼只是永遠運行。 我在使用monads時遇到過這個問題,我想知道是否有一個解決方法可用於我遇到的這個問題。
謝謝!
代碼永遠運行,因為您試圖在調用mapM
對無限多個副作用計算進行mapM
。
由於您catMaybes
並不知道需要運行多少這些計算(如使用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
可能有更優雅的方式,但它現在不是我的意思:)
有時懶惰的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
返回一個懶惰的結果流,我們采取盡可能多的結果。 簡單。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.