7

我试图永远重复一个 IO 操作,但是将一个执行的结果输入到下一个执行中。像这样的东西:

-- poorly named
iterateM :: Monad m => (a -> m a) -> a -> m b
iterateM f a = f a >>= iterateM f

Hoogle 似乎对我没有帮助,但我看到很多功能看起来非常接近我想要的功能,但似乎没有一个功能完全符合我的要求。

4

4 回答 4

4

你是对的,我不知道这种特殊循环在哪里实现。你的实现看起来不错;为什么不将它作为补丁提交给monad-loops包呢?

于 2012-05-29T17:16:12.457 回答
4

好吧,我希望iterateM组合器具有这种类型签名:

iterateM :: (Monad m) => (a -> m a) -> a -> m [a]

当然,这不是一个非常有用的组合器,因为您无法在大多数 monad 中提取结果。与组合器的基本命名标准一起使用的更明智的名称是iterateM_

iterateM_ :: (Monad m) => (a -> m a) -> a -> m b
iterateM_ f = fix $ \again x -> f x >>= again

这个组合器很有用:

countFrom :: (Enum a) => a -> IO b
countFrom = iterateM_ (\x -> succ x <$ print x)

但是,为了简单起见,我只会使用fix或显式递归。显式递归代码不会更长或更不可读:

countFrom :: (Enum a) => a -> IO b
countFrom = fix (\again x -> print x >> again (succ x))
于 2012-05-29T15:08:25.543 回答
3

我相信您在标准库中看不到它的原因是它永远不会终止。iterate 函数可以利用惰性列表来允许您使用take结果列表上的函数指定终止。在这里,你的结果是一元的,所以这是不可能的。

显然你的想法的精神是可以做到的。它只是看起来有点不同:

iterateM :: Monad m => Int -> (a -> m a) -> a -> m a
iterateM 0 _ a = return a
iterateM n f a = f a >>= iterateM (n-1) f
于 2012-05-29T14:33:03.583 回答
1

这实际上可以foreverStateT.

import Control.Monad.Trans.State
import Control.Monad.Trans.Class (lift)
import Control.Monad (forever)

iterateM :: Monad m => (a -> m a) -> a -> m b
iterateM f = evalStateT $ forever $ get >>= lift . f >>= put
于 2012-05-29T18:33:01.153 回答