在 Haskell 中延迟评估一元函数

发布于 2024-11-05 15:52:04 字数 385 浏览 5 评论 0原文

我似乎无法找到解决我遇到的这个问题的方法。

我有这样的事情:

  getFilePathForDay :: Day -> IO (Maybe FilePath)

  getFilePathForDays date days = do
      files <- mapM getFilePathForDay $ iterate (addDays 1) date
      return . take days . catMaybes $ files

我试图获取 x 数量的有效文件路径,其中 x 是我想要的天数,但上面的代码只是永远运行。我之前在使用 monad 时遇到过这个问题,我想知道是否有解决方法可以解决我遇到的这个问题。

谢谢!

I can't seem to figure out a workaround for this issue i'm having.

I have something like this:

  getFilePathForDay :: Day -> IO (Maybe FilePath)

  getFilePathForDays date days = do
      files <- mapM getFilePathForDay $ iterate (addDays 1) date
      return . take days . catMaybes $ files

I am trying to get x amount of valid file paths where x is the number of days I want, but the above code just runs forever. I have ran into this problem before with monads and I was wondering if there is a workaround available for this issue I am having.

Thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

笑红尘 2024-11-12 15:52:04

该代码会永远运行,因为您试图在调用 mapM 时对无限数量的副作用计算进行排序。

由于您事先不知道需要运行多少次计算(如使用 catMaybes 所示),因此您必须交错调用 getFilePathForDay 与其余的计算。这可以使用 unsafeInterleaveIO 来完成,但顾名思义,这不是推荐的方法。

您可以手动实现此方法:

getFilePathForDays _ 0 = return []
getFilePathForDays date days = do
    mpath <- getFilePathForDay date
    case mpath of
        Just path -> (path :) <
gt; remaining (days-1)
        Nothing   -> remaining days
  where
    remaining days = getFilePathForDays (addDays 1 date) days

可能有一种更优雅的方法,但我现在还没有想到:)

The code runs forever because you are trying to sequence an infinite number of side-effecting computations in your call to mapM.

Since you don't know ahead of time how many of these computations you will need to run (as indicated by your use of catMaybes), you will have to interleave the calls to getFilePathForDay with the rest of your computation. This could be done using unsafeInterleaveIO, but as the name suggests, this is not a recommended approach.

You could implement this manually as:

getFilePathForDays _ 0 = return []
getFilePathForDays date days = do
    mpath <- getFilePathForDay date
    case mpath of
        Just path -> (path :) <
gt; remaining (days-1)
        Nothing   -> remaining days
  where
    remaining days = getFilePathForDays (addDays 1 date) days

There is probably a more elegant way, but it's not coming to me right now :)

烛影斜 2024-11-12 15:52:04

有时惰性 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 返回一个惰性结果流,我们可以根据需要获取任意数量的结果。简单的。

Sometimes lazy IO is the right approach. Since you have an infinite, lazy sequence of days, and you're sampling, lazily, some (dynamic) range from that sequence, we should also hit the filesystem only on demand.

This can be done with 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

Where go returns a lazy stream of results, and we take as many as we need. Easy.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文