哈斯克尔:monadic takeWhile? [英] Haskell: monadic takeWhile?

查看:154
本文介绍了哈斯克尔:monadic takeWhile?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一些用C编写的函数,我从Haskell调用。这些函数返回 IO(CInt)。有时我想运行所有的功能,而不管它们中的任何一个返回,这很容易。出于示例代码的原因,这是目前发生的一般想法:

  Prelude>让f x = print x>>返回x 
前奏> mapM_f [0..5]
0
1
2
3
4
5
Prelude>

我得到了我想要的副作用,我不关心结果。但是现在我需要在第一个没有返回我想要的结果的项目之后立即停止执行。假设一个4或更高的返回值需要执行停止 - 那么我想要做的是这样的:

 前奏> takeWhile(<4)$ mapM f [0..5] 

这给了我这个错误:

 
< interactive>:1:22:
无法匹配预期类型`[b] IO a'
在`mapM'的第一个参数中,即`f'
在`($)'的第二个参数中,即`mapM f([0..5])'
在表达式中:takeWhile(< 4)$ mapM f([0..5])

这对我来说很有意义 - 仍然包含在IO monad中,我不能只比较IO monad中包含的两个值。我知道这正是monad的目的 - 在满足某些条件时将结果链接在一起并放弃操作 - 但是在这种情况下是否有一种简单方法来封装IO monad以在条件停止执行链我的选择,而不写一个 MonadPlus 的实例?

我可以只为takeWhile的目的取消来自 f 的值吗?

这是一个解决方案吗?玩家还没有点击我,但我有一种印象,这可能是一个很好的情况下使用它们。





更新:



@sth与我想要的有最接近的答案 - 事实上,这几乎就是我所要做的,但我仍然想看看是否存在不是显式递归的标准解决方案 - 毕竟这是Haskell!回顾一下我如何解释我的问题,现在我可以看到我对自己期望的行为不够清楚。


$ b $ <<> c $ c> f <上面例子中使用的code>函数仅仅是一个例子。真正的功能是用C编写的,专门用于它们的副作用。我不能使用@ Tom对 mapM_ f(takeWhile(< 4)[0..5])的建议,因为我不知道是否有任何输入真的会导致成功或失败,直到执行。



我实际上并不关心返回的列表,我只是想调用C函数,直到列表被耗尽或第一个C函数返回一个失败代码。



在C风格的伪代码中,我的行为是:

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

再次,@ sth的答案执行我想要的确切行为,除了结果可能(应该?)被丢弃。一个 dropWhileM _ 函数对我而言是等价的。为什么在Control.Monad中没有这样的函数或 takeWhileM _ ?我发现在邮件列表中类似的讨论,但似乎没有任何结果。

解决方案

您可以定义 sequence 作为

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

liftM2 您已经看到的是您没有机会停止 m2 ,它可能是 launchTheMissiles

  liftM2 ::(Monad m)=> (a→b→c)→> m a  - > m b  - > mc 
liftM2 f m1 m2 = do
x1 < - m1
x2 < - m2
return(f x1 x2)

使用 guard ,如下所示:

  sequenceUntil p xs = foldr(myLiftM2 p(:))(return [])xs 
其中myLiftM2 pf m1 m2 = do
x1 < - m1
guard $ p x1
x2 < - m2
return(f x1 x2)

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

所以握住它的手多一点点$ / b
$ b

 模块Main其中

导入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
其中myLiftM2 fz m1 m2 = do
x1 < - m1
如果p x1则x2 < - m2
返回$ f x1 x2
else返回z

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

尽管 as 是1到10之间的动作列表,输出是

  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 ..] ,它显示新的组合器保持懒惰






您可能更喜欢 spanM

  spanM ::(Monad m)=> (a  - > Bool) - > [m a]  - > m([a],[ma])
spanM _ [] = return([],[])
spanM p(a:as)= do
x< - a
if px then do(xs,bs)< - spanM p as
return(x:xs,bs)
else return([x],as)

请注意,它与 span ,因为它包含结果列表中的失败元素。这一对是剩下的动作。例如:

  * Main> (xs,bs)< -  spanM(<4)为
1
2
3
4
* Main> xs
[1,2,3,4]
* Main>序列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
除非(py)$ untilM p xs

请注意,谓词的意义得到补充:

  *主>直到M(> = 4)为
1
2
3
4


I have some functions written in C that I call from Haskell. These functions return IO (CInt). Sometimes I want to run all of the functions regardless of what any of them return, and this is easy. For sake of example code, this is the general idea of what's happening currently:

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

I get my desired side effects, and I don't care about the results. But now I need to stop execution immediately after the first item that doesn't return my desired result. Let's say a return value of 4 or higher requires execution to stop - then what I want to do is this:

Prelude> takeWhile (<4) $ mapM f [0..5]

Which gives me this error:

<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);

So again, @sth's answer performs the exact behavior that I want, except that the results may (should?) be discarded. A dropWhileM_ function would be equivalent for my purposes. Why isn't there a function like that or takeWhileM_ in Control.Monad? I see that there was a similar discussion on a mailing list, but it appears that nothing has come of that.

解决方案

You might define sequence as

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

The problem with liftM2 that you've been seeing is you don't have an opportunity to stop m2, which might be 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)

Using guard as in the following seems appealing:

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)

The code above will fail in your application because the IO monad is not an instance of MonadPlus.

So hold its hand a little more

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

Even though as is a list of actions over 1 to 10, the output is

1
2
3
4
[1,2,3]

Discarding the results is then trivial:

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

Note the use of [1..] that shows the new combinator maintains laziness.


You may prefer 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)

Note that it differs slightly from span in that it includes the failing element in the result list. The pair's second is the remaining actions. For example:

*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]


Yet another alternative:

untilM :: Monad m => (a -> Bool) -> [m a] -> m ()
untilM p (x:xs) = do
  y <- x
  unless (p y) $ untilM p xs

Note that the sense of the predicate is complemented:

*Main> untilM (>= 4) as
1
2
3
4

这篇关于哈斯克尔:monadic takeWhile?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆