gpt4 book ai didi

Haskell: monadic takeWhile?

转载 作者:行者123 更新时间:2023-12-04 00:00:44 26 4
gpt4 key购买 nike

我有一些从 Haskell 调用的用 C 语言编写的函数。这些函数返回 IO (CInt) .有时我想运行所有函数,而不管它们返回什么,这很容易。为了示例代码,这是当前正在发生的事情的一般概念:

Prelude> let f x = print x >> return x
Prelude> mapM_ f [0..5]
0
1
2
3
4
5
Prelude>

我得到了我想要的副作用,我不在乎结果。但是现在我需要在第一个没有返回我想要的结果的项目之后立即停止执行。假设返回值 4 或更高需要停止执行 - 那么我想要做的是:
Prelude> takeWhile (<4) $ mapM f [0..5]

这给了我这个错误:
<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 .. 5])

And that makes sense to me - the result is still contained in the IO monad, and I can't just compare two values contained in the IO monad. I know this is precisely the purpose of monads -- chaining results together and discarding operations when a certain condition is met -- but is there an easy way to "wrap up" the IO monad in this case to stop executing the chain upon a condition of my choosing, without writing an instance of MonadPlus?

Can I just "unlift" the values from f, for the purposes of the takeWhile?

Is this a solution where functors fit? Functors haven't "clicked" with me yet, but I sort of have the impression that this might be a good situation to use them.


Update:

@sth has the closest answer to what I want - in fact, that's almost exactly what I was going for, but I'd still like to see whether there is a standard solution that isn't explicitly recursive -- this is Haskell, after all! Looking back on how I worded my question, now I can see that I wasn't clear enough about my desired behavior.

The f function I used above for an example was merely an example. The real functions are written in C and used exclusively for their side effects. I can't use @Tom's suggestion of mapM_ f (takeWhile (<4) [0..5]) because I have no idea whether any input will really result in success or failure until executed.

I don't actually care about the returned list, either -- I just want to call the C functions until either the list is exhausted or the first C function returns a failure code.

In C-style pseudocode, my behavior would be:

do {
result = function_with_side_effects(input_list[index++]);
} while (result == success && index < max_index);

同样,@sth 的答案执行了我想要的确切行为,除了结果可能(应该?)被丢弃。一个 dropWhileM_功能对于我的目的来说是等效的。为什么没有这样的功能或 takeWhileM_在 Control.Monad 中?我看到有 a similar discussion on a mailing list ,但似乎什么都没有发生。

最佳答案

您可以定义 sequence作为

sequence xs = foldr (liftM2 (:)) (return []) xs

liftM2 的问题你一直看到的是你没有机会停下来 m2 ,可能是 launchTheMissiles !
liftM2 :: (Monad m) => (a -> b -> c) -> m a -> m b -> m c
liftM2 f m1 m2 = do
x1 <- m1
x2 <- m2
return (f x1 x2)

使用 guard 如下所示似乎很有吸引力:
sequenceUntil p xs = foldr (myLiftM2 p (:)) (return []) xs
where myLiftM2 p f m1 m2 = do
x1 <- m1
guard $ p x1
x2 <- m2
return (f x1 x2)

上面的代码将在您的应用程序中失败,因为 IO monad 不是 MonadPlus 的实例.

所以多握住它的手
module Main where

import Control.Monad

printx :: Int -> IO Int
printx x = do
print x
return x

sequenceUntil :: (Monad m) => (a -> Bool) -> [m a] -> m [a]
sequenceUntil p xs = foldr (myLiftM2 (:) []) (return []) xs
where myLiftM2 f z m1 m2 = do
x1 <- m1
if p x1 then do x2 <- m2
return $ f x1 x2
else return z

main :: IO ()
main = do
let as :: [IO Int]
as = map printx [1..10]
ys <- sequenceUntil (< 4) as
print ys

即使 as是超过 1 到 10 的 Action 列表,输出为
1
2
3
4
[1,2,3]

丢弃结果是微不足道的:
sequenceUntil_ :: (Monad m) => (a -> Bool) -> [m a] -> m ()
sequenceUntil_ p xs = sequenceUntil p xs >> return ()

main :: IO ()
main = do
let as :: [IO Int]
as = map printx [1..]
sequenceUntil_ (< 4) as

注意 [1..] 的使用显示新的组合器 maintains laziness .

您可能更喜欢 spanM :
spanM :: (Monad m) => (a -> Bool) -> [m a] -> m ([a], [m a])
spanM _ [] = return ([], [])
spanM p (a:as) = do
x <- a
if p x then do (xs,bs) <- spanM p as
return (x:xs, bs)
else return ([x], as)

请注意,它与 span 略有不同。因为它在结果列表中包含失败的元素。对的第二个是剩下的 Action 。例如:
*Main> (xs,bs) <- spanM (< 4) as
1
2
3
4
*Main> xs
[1,2,3,4]
*Main> sequence bs
5
6
7
8
9
10
[5,6,7,8,9,10]

还有另一种选择:
untilM :: Monad m => (a -> Bool) -> [m a] -> m ()
untilM p (x:xs) = do
y <- x
unless (p y) $ untilM p xs

注意谓词的意义是互补的:
*Main> untilM (>= 4) as
1
2
3
4

关于Haskell: monadic takeWhile?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1133800/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com