【问题标题】:Data.IORef vs RecursionData.IORef 与递归
【发布时间】:2015-08-15 07:22:31
【问题描述】:

数据.IORef

keepRunning <- newIORef True
let loop = do
    running <- readIORef keepRunning
    if running then
        do
            {- Do stuff -}
            loop running
            return ()
    else
        do
            {- Do stuff -}
            return ()

递归

let keepRunning = True
let loop running = if running then
    do
        {- Do stuff -}
        loop running
        return ()
    else do
        {- Do stuff -}
        return ()
loop keepRunning

有什么理由更喜欢上述方法之一吗?与 do 块外相比,包含递归的 do 块会减慢程序速度吗?如果可以用函数替换Data.IORef 的每个实例,那为什么Data.IORef 还存在呢?

【问题讨论】:

  • 您不能“用函数替换 Data.IORef 的每个实例”——您为什么相信这一点?在一些简单的情况下你可以,但通常你不能。也许您可以执行整个程序转换以消除所有IORefs,但这种转换不是本地的,并且需要大量的状态传递。
  • IORef 对并发很有用。我最近还将它与一些 FFI 代码一起使用,其 API 要求我传递一些回调 :: Foo -&gt; IO () 以被多次调用(因此 IORef 用于跟踪状态)。当递归就足够时不使用它的原因与隐式状态使程序更加混乱、容易出错、难以重构等原因都是相同的。

标签: variables haskell recursion io reference


【解决方案1】:

如果您在第一个版本中的{- Do stuff -} 直接将keepRunning 分配给某些计算的结果,您当然可以轻松地重写它以使该计算成为递归的条件并摆脱IORef。这将是更可取的解决方案。

但是,如果您的第一个版本在其他函数中设置了keepRunning,则等效的无引用版本可能会变得相当复杂。因此,在这种情况下,哪个版本更可取就不太清楚了。虽然你可以指出这样的事情一开始就是糟糕的设计。如果不知道 {- Do stuff -} 实际做了什么,就很难说出具体的内容。

与 do 块外相比,包含递归的 do 块会减慢程序速度吗?

没有。

如果可以用函数替换 Data.IORef 的每个实例,那为什么 Data.IORef 还存在?

根据您使用的 API,您可能无法做到(假设您在非 FRP GUI 库中有事件处理程序)。此外,在某些情况下,您可以删除 IORef,但只能通过损害性能和/或使代码更复杂。

也就是说,您使用IORef 作为循环条件的示例是您几乎可以肯定在没有IORef 的情况下编写更好的版本。

【讨论】:

  • 在dostuff里面,也就是
  • @YoYoYonnY 实际上确实如此 - 我的错误。我想我假设loop running 中的running 应该引用您作为参数收到的同一变量running。但你是对的,如果你对不同的变量重复使用相同的名称,代码可能会终止。然而,这使得参数首先变得毫无意义——你也可以在递归调用周围包裹一个if
  • 是的...我只是认为这将是一个更好的演示,因为两者非常相似。
  • running &lt;- return False,略有不同。
猜你喜欢
  • 1970-01-01
  • 2010-12-23
  • 2011-03-03
  • 1970-01-01
  • 1970-01-01
  • 2011-03-19
  • 2012-10-21
  • 1970-01-01
  • 2017-01-08
相关资源
最近更新 更多