我有一些用C语言编写的函数,我从Haskell调用.这些功能返回IO (CInt).有时我想运行所有函数,无论它们返回什么,这很容易.为了示例代码,这是当前正在发生的事情的一般概念:
Prelude> let f x = print x >> return x
Prelude> mapM_ f [0..5]
0
1
2
3
4
5
Prelude>
Run Code Online (Sandbox Code Playgroud)
我得到了我想要的副作用,我不关心结果.但是现在我需要在第一个没有返回我想要的结果的项目之后立即停止执行.假设返回值为4或更高要求执行停止 - 那么我想要做的是:
Prelude> takeWhile (<4) $ mapM f [0..5]
Run Code Online (Sandbox Code Playgroud)
这给了我这个错误:
<interactive>:1:22:
Couldn't match expected type `[b]' against inferred type `IO a'
In the first argument of `mapM', namely `f'
In the second argument of `($)', namely `mapM f ([0 .. 5])'
In the expression: takeWhile (< 4) $ mapM f ([0 … 下面的简单函数迭代地应用给定的monadic函数,直到它命中Nothing,此时它返回最后一个非Nothing值.它做我需要的,我理解它是如何工作的.
lastJustM :: (Monad m) => (a -> m (Maybe a)) -> a -> m a
lastJustM g x = g x >>= maybe (return x) (lastJustM g)
Run Code Online (Sandbox Code Playgroud)
作为我在Haskell的自我教育的一部分,我试图尽可能避免明确的递归(或至少理解如何).在这种情况下,似乎应该有一个简单的非显式递归解决方案,但我无法搞清楚.
我不希望像一个一元版本的takeWhile,因为它可以收集所有没有预值昂贵,反正我不关心他们.
我检查了Hoogle的签名,没有任何显示.这个m (Maybe a)位让我觉得monad变换器在这里可能很有用,但我真的不具备我需要提出细节的直觉(还).
这可能要么是令人尴尬地容易做到这一点,要么令人尴尬地容易理解为什么不能或不应该这样做,但这不是我第一次将自我尴尬作为一种教学策略.
更新:我当然可以提供谓词而不是使用Maybe:类似(a -> Bool) -> (a -> m a) -> a(返回谓词为真的最后一个值)也可以正常工作.我感兴趣的是使用标准组合器编写没有显式递归的任一版本的方法.
背景:这是一个简化的上下文工作示例:假设我们对单位广场中的随机游走感兴趣,但我们只关心退出点.我们有以下步骤功能:
randomStep :: (Floating a, Ord a, Random a) =>
a -> (a, a) -> State StdGen (Maybe (a, a))
randomStep s (x, y) …Run Code Online (Sandbox Code Playgroud)