【问题标题】:Composing monad actions with folds用折叠组合单子动作
【发布时间】:2011-01-15 06:17:16
【问题描述】:

让我们使用(Monad m) => a -> m a 类型的函数。例如:

ghci> let f x = Just (x+1)

我希望能够多次应用它。我尝试的第一件事是

ghci> let times n f = foldr (>=>) return $ replicate n f

问题是它不适用于大型n

ghci> 3 `times` f $ 1
Just 4
ghci> 1000000 `times` f $ 1
Just *** Exception: stack overflow

反之亦然:

ghci> let timesl n f = foldl' (<=<) return $ replicate n f
ghci> 3 `timesl` f $ 1
Just 4
ghci> 1000000 `timesl` f $ 1
Just *** Exception: stack overflow

实际上,使用($!)严格操作符的工作原理

ghci> let timesStrict n f = foldr1 ((>=>) . ($!)) $ replicate n f
ghci> 3 `timesStrict` f $ 1
Just 4
ghci> 10000000 `timesStrict` f $ 1
Just 10000001

有更好或更惯用的解决方案吗?或者可能是更严格的?如果f 是重量级函数,我仍然很容易发生堆栈溢出。

UPD: 我发现写times 以一种有针对性的形式也不能解决编写重量级单子动作的问题。这适用于 f x = Just (x+1) 但在现实世界中失败:

times f 0 a = return a
times f i a = (f $! a) >>= times f (i - 1)

【问题讨论】:

    标签: haskell lazy-evaluation monads reduce


    【解决方案1】:

    我想出了这个:

     last $ take n $ iterate (>>= f) $ Just 1
    

    但它也会在大量n 上溢出堆栈。我现在没有时间深入研究它:-(

    【讨论】:

      【解决方案2】:

      我可能会为现有函数创建一些更严格的变体。

      {-# LANGUAGE BangPatterns #-}
      iterate' f !x = x : iterate' f (f x)
      ma >>=! f = do !a <- ma; f a
      times' n f a = iterate' (>>=! f) (return a) !! n
      

      也许您的问题源于seq 仅评估WHNF 的第一个参数?如果您正在处理复杂的结构,您可能需要更深的seq,例如deepseq

      【讨论】:

      • 这个解决方案似乎运作良好,但并不比 timesStrict 好,因此它也不是可扩展的。我必须研究 deepseq。谢谢。
      【解决方案3】:

      如果你将f 设置为严格

      f x = let y = x+1 in y `seq` Just y
      

      -- remember to enable -XBangPatterns
      f !x = Just (x+1)
      

      不用管其余部分,即使n 非常大,您的代码也会在恒定空间中运行(尽管速度很慢):

      ghci> 乘以 4000000000 f 3
      只需 4000000003

      【讨论】:

      • 好吧,如果你运行更多的迭代,仍然是同样的问题:ghci> iterateM_n 1000000 (Just . (+1)) 3 \n Just *** Exception: stack overflow \n ghci> iterateM_n' 1000000 (+) 0 (Just . (+1)) 3 \n Just *** 例外:堆栈溢出\n
      • 我喜欢使用-XBangPatterns 而不是seq :-) 无论如何,如果f 是严格的,那么我的回答中就不需要&gt;&gt;=!。由于似乎 OP 的 f 不是,这可能会有所帮助。
      • 谢谢!公认。这确实适用于 (Just . (+1)) 。我的实际功能仍然存在问题,但至少现在我知道有什么可以帮助的了。
      • @jetxee 谢谢! “我如何使这个功能变得严格?”似乎是一个很好的后续问题。
      猜你喜欢
      • 2012-01-05
      • 1970-01-01
      • 1970-01-01
      • 2015-11-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-01-11
      • 2019-01-06
      相关资源
      最近更新 更多