有人指出Is this a case for foldM?可能是重复的。现在,我有一个强烈的意见,即可以用相同答案回答的两个问题不一定是重复的! "什么是1 - 2"和"什么是我^ 2"两者都产生" -1"但不,它们不是重复的问题。我的问题(已经回答,类似)是关于"函数iterateM
是否存在于Haskell标准库"中,而不是"如何实现链式monad操作"。
当我写一些项目时,我发现自己编写了这个组合器:
repeatM :: Monad m => Int -> (a -> m a) -> a -> m a
repeatM 0 _ a = return a
repeatM n f a = (repeatM (n-1) f) =<< f a
它只执行monadic动作n
次,将之前的结果提供给下一个动作。我尝试了一些hoogle
搜索和一些Google搜索,但没有找到任何附带&#34;标准&#34;哈斯克尔。是否有预定义的正式功能?
答案 0 :(得分:10)
您可以使用foldM
,例如:
import Control.Monad
f a = do print a; return (a+2)
repeatM n f a0 = foldM (\a _ -> f a) a0 [1..n]
test = repeatM 5 f 3
-- output: 3 5 7 9 11
答案 1 :(得分:9)
Carsten mentioned replicate
,这并不是一个糟糕的想法。
import Control.Monad
repeatM n f = foldr (>=>) pure (replicate n f)
这背后的想法是,对于任何monad m
,a -> m b
类型的函数形成了m
的Kleisli类别,带有标识箭头
pure :: a -> m a
(也称为return
)
和作曲家
(<=<) :: (b -> m c) -> (a -> m b) -> a -> m c
f <=< g = \a -> f =<< g a
由于实际上处理类型为a -> m a
的函数,我们实际上正在查看Kleisli类别的一个monoid,因此我们可以考虑折叠这些箭头的列表。 / p>
上面的代码所做的是将合成运算符折叠成n
f
个x >=> y >=> z >=> w
副本列表,像往常一样以身份完成。翻转合成操作符实际上使我们进入了双重类别;对于许多常见的monad,w <=< z <=< y <=< x
比<=<
更有效;因为在这种情况下所有的箭头都是相同的,所以我们似乎也可以。请注意,对于延迟状态monad以及读者monad,最好使用未翻转的>=>
运算符;对于IO
,ST s
和通常的严格状态, <receiver android:name="android.app.admin.DeviceAdminReceiver"
android:label="@string/admin_app"
android:description="@string/admin_app_description"
android:permission="android.permission.BIND_DEVICE_ADMIN">
<meta-data android:name="android.app.device_admin" android:resource="@xml/admin_app_config"/>
<intent-filter>
<action android:name="android.app.action.DEVICE_ADMIN_ENABLED"/>
</intent-filter>
</receiver>
通常会更好。
注意:我不是类别理论家,所以上面的解释可能有错误。
答案 2 :(得分:2)
我发现自己经常想要这个功能,我希望它有一个标准名称。但是,该名称不会是repeatM
- 这将是无限重复,如果它存在,就像forever
一样,只是为了与其他库保持一致(并且repeatM
在某些库中定义)。
正如已经给出的答案的另一个观点,我指出(s -> m s)
看起来有点像状态类型为s
的状态monad中的一个动作。
实际上,它与StateT s m ()
是同构的 - 一个不返回任何值的动作,因为它所做的所有工作都以它改变状态的方式封装。在这个monad中,你想要的功能是replicateM
。你可以用这种方式在haskell中编写它,虽然它可能看起来比直接写它更丑。
首先将s -> m s
转换为StateT
使用的等效表单,添加无信息()
,使用liftM
在返回类型上映射函数。
> :t \f -> liftM (\x -> ((),x)) . f
\f -> liftM (\x -> ((),x)) . f :: Monad m => (a -> m t) -> a -> m ((), t)
(本来可以使用fmap
,但Monad约束在这里看起来更清晰;如果你愿意,可以使用TupleSections
;如果你发现表示法更容易阅读,那么它只是\f s -> do x <- f s; return ((),s)
)。
现在这个包含StateT的正确类型:
> :t StateT . \f -> liftM (\x -> ((),x)) . f
StateT . \f -> liftM (\x -> ((),x)) . f :: Monad m => (s -> m s) -> StateT s m ()
然后你可以使用replicateM_
版本复制它n次,因为来自[()]
的返回列表replicateM
不会很有趣:
> :t \n -> replicateM_ n . StateT . \f -> liftM (\x -> ((),x)) . f
\n -> replicateM_ n . StateT . \f -> liftM (\x -> ((),x)) . f :: Monad m => Int -> (s -> m s) -> StateT s m ()
最后你可以使用execStateT
回到你最初工作的Monad:
runNTimes :: Monad m => Int -> (s -> m s) -> s -> m s
runNTimes n act =
execStateT . replicateM_ n . StateT . (\f -> liftM (\x -> ((),x)) . f) $ act