使用折叠组合 monad 动作

发布于 2024-08-20 13:15:47 字数 1119 浏览 18 评论 0原文

让我们采用 (Monad m) => 类型的函数一个-> 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

实际上,有效的是使用 ($!)< /code> 严格运算符

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也不能解决组合重量级单子动作的问题。这适用于 fx = Just (x+1) 但在现实世界中失败:

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

Let's take a function of type (Monad m) => a -> m a. For example:

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

I'd like to be able to apply it any number of times. The first thing I tried was

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

The problem is that it won't work for large n:

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

It doesn't work also the other way:

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

Actually, what works is using ($!) strictness operator

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

Is there a nicer or more idiomatic solution? Or probably a stricter one? I still easily get stack overflows if f is a heavy-weight function.

UPD: I found that writing times in a pointful form does not solve the problem of composing heavy-weight monadic actions neither. This works for f x = Just (x+1) but fails in the real world:

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

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

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

发布评论

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

评论(3

扭转时空 2024-08-27 13:15:47

如果您将 f 设置为像

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

or

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

一样严格,而保留其余部分,则即使 n 非常大,您的代码也会在恒定空间中运行(尽管速度很慢):

ghci> times 4000000000 f 3
Just 4000000003

If you make f strict as in

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

or

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

and leave the rest alone, your code runs in constant space (albeit slowly) even with very large n:

ghci> times 4000000000 f 3
Just 4000000003
浅浅 2024-08-27 13:15:47

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

{-# 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,例如 深度序列

I'd probably create some stricter variants of existing functions.

{-# 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

Perhaps your problems stem from the fact that seq only evaluates the first argument to WHNF? If you're working on a complex structure, you may need a deeper seq, like deepseq.

冷默言语 2024-08-27 13:15:47

我想出了这个:

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

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

I came up with this:

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

But it also overflows the stack on large numbers of n. I don't have the time right now to look into it more :-(

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