我们采用(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
并不能解决构成重量级monadic动作的问题。这适用于f x = Just(x + 1)但在现实世界中失败:
times f 0 a = return a
times f i a = (f $! a) >>= times f (i - 1)
答案 0 :(得分:4)
如果你像{/ p>那样严格f
f x = let y = x+1 in y `seq` Just y
或
-- remember to enable -XBangPatterns
f !x = Just (x+1)
并且剩下的就是其余部分,即使是非常大的n
,您的代码也会在恒定的空间中运行(尽管速度很慢):
ghci> times 4000000000 f 3 Just 4000000003
答案 1 :(得分: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。
答案 2 :(得分:1)
我想出了这个:
last $ take n $ iterate (>>= f) $ Just 1
但它也会在大量n
上溢出堆栈。我现在没有时间更多地研究它: - (