Haskell:单子 takeWhile?

2023-11-21

我有一些用 C 编写的函数,我从 Haskell 调用它们。这些函数返回IO (CInt)。有时我想运行所有函数,无论它们返回什么,这很容易。为了示例代码,这是当前发生的情况的总体思路:

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

我得到了我想要的副作用,而且我不在乎结果。但现在我需要在第一个项目未返回我想要的结果后立即停止执行。假设返回值 4 或更高需要执行停止 - 那么我want要做的是:

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

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

我可以“取消”其中的值吗f,为了 takeWhile 的目的?

这是函子适合的解决方案吗?函子还没有与我“合拍”,但我有这样的印象:这可能是使用它们的好情况。


Update:

@sth 对我想要的有最接近的答案 - 事实上,这几乎正是我想要的,但我仍然想看看是否有standard不是显式递归的解决方案——毕竟这是 Haskell!回顾我如何措辞我的问题,现在我发现我对自己想要的行为还不够清楚。

The f我上面用于示例的函数只是一个示例。真正的函数是用 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 的答案执行了我想要的确切行为,除了结果可能(应该?)被丢弃。 AdropWhileM_对于我的目的来说,函数是等效的。为什么没有这样的功能或者takeWhileM_在 Control.Monad 中?我看到有邮件列表上的类似讨论,但似乎没有任何结果。


你可能会定义sequence as

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)

Using 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 不是莫纳德加.

所以多握住它的手一点

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 之间的操作列表,输出为

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], [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因为它在结果列表中包含失败的元素。两人的第二个任务是剩下的行动。例如:

*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
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Haskell:单子 takeWhile? 的相关文章

  • 简单的秒差距示例会产生类型错误

    我正在尝试编译这个简单的秒差距代码 import Text Parsec simple letter 但我不断收到此错误 No instance for Stream s0 m0 Char arising from a use of let
  • 如何处理或避免BlockedIndefinitelyOnSTM异常?

    我花了很多时间来解决我正在处理的应用程序中遇到的问题 该应用程序是一个 Web 应用程序 使用 scotty 公开 REST 端点 它使用一个TVar保持其更新的状态STM a由前端层触发的动作 由于该应用程序基于事件溯源原则 因此业务层生
  • Haskell 中的多态函数作为参数

    我有一个带有两个构造函数的 ADT 一个包裹着一个Double和一个包裹着Integer 我想创建一个函数 它采用一元函数Numtypeclass 并返回一个函数 该函数将该一元函数应用于我的 ADT 的内容 我试过这个 data X Y
  • 为什么我不能将 Int 类型与 a 类型匹配

    哈斯克尔新手在这里 我在这里尝试做的事情的一个过于简单的例子 test Int gt a test i i Couldn t match expected type a with actual type Int a is a rigid t
  • Repa 数组上的并行 mapM

    在我最近的work https github com bgamari mixture model with Gibbs sampling 我一直在充分利用RVar http hackage haskell org packages arch
  • 用纯函数式语言保持状态

    我正在尝试弄清楚如何执行以下操作 假设您正在开发直流电机的控制器 您希望让它以用户设置的特定速度旋转 def set point ref sp 90 while true let curr read speed controller set
  • 作为应用函子(Haskell / LYAH)

    第11章向你学习 Haskell引入以下定义 instance Applicative gt r where pure x gt x f lt gt g x gt f x g x 在这里 作者进行了一些不寻常的挥手 的实例实现有点神秘 所以
  • 不明白这个 haskell 代码中的内容

    我有一些 Haskell 代码 我正在尝试完成它 但我不明白其中发生了什么 type Bag a a gt Int emptyB Bag a emptyB e gt 0 countB Eq a gt Bag a gt a gt Int co
  • haskell复制目录的方法是什么

    我发现自己用 Haskell 编写越来越多的脚本 但在某些情况下 我真的不确定如何 正确 地做到这一点 例如递归地复制目录 a la unixcp r 由于我主要使用 Linux 和 Mac OS 所以我通常会作弊 import Syste
  • 系统地将函数应用于 haskell 记录的所有字段

    我有一条包含不同类型字段的记录 以及一个适用于所有这些类型的函数 举一个小 愚蠢 的例子 data Rec Rec flnum Float intnum Int deriving Show 比如说 我想定义一个为每个字段添加两条记录的函数
  • 为什么 Kleisli 不是 Monoid 的一个实例?

    如果您希望附加两个类型 a gt m b 的函数 以便只得到一个附加两个结果的相同类型的函数 您可以使用 Kleisli 来执行此操作 instance Monad m Monoid b gt Monoid Kleisli m a b wh
  • 无法让 wxHaskell 在 Mac 上从 ghci 工作

    我正在尝试跑步一个例子 http www haskell org haskellwiki WxHaskell Quick start Hello world in wxHaskell using EnableGUI function htt
  • 使用 Parsec 解析数据并省略注释

    我正在尝试编写一个 Haksell Parsec 解析器 它将文件中的输入数据解析为 LogLine 数据类型 如下所示 Final parser that holds the indvidual parsers final Parser
  • 优化计算 200 万以下所有素数总和的 Haskell 代码

    欧拉计划中的问题 10 我在那里看到了一些讨论 但仅限于 C 我用下面的代码来计算 print sum sieve 2 2000000 where sieve sieve x xs x sieve filter 0 mod x xs 需要很
  • 关注点分离:什么时候最好将语义与语法分离?

    Choices 类型类的出色之处在于它们允许我们将额外的结构连接到现有类型 从而使我们能够推迟一些设计决策 而不是在构思时匆忙做出决定 另一方面 例如 在面向对象编程中 我们被迫考虑类型需要立即执行什么操作 以及稍后出现的或需要的任何附加结
  • Haskell 中的 Monad 和 Purity

    我的问题是 Haskell 中的 monad 是否真正保持了 Haskell 的纯度 如果是的话 又是如何保持的 我经常读到副作用是如何不纯粹的 但有用的程序 例如 I O 需要副作用 下一句指出 Haskell 对此的解决方案是 mona
  • 为什么阴谋集团重新安装“总是危险的”?

    使用 Cabal 重新安装软件包时 通常会看到以下警告 警告 请注意 重新安装总是很危险的 无论如何继续 此消息背后的一些原因是什么 目前 重新安装软件包意味着破坏性地覆盖已安装的软件包 如果旧包对系统有任何反向依赖性 它们将不再工作 为了
  • 如何编写将布尔值返回到一个函数的函数

    我在这里发现了一个类似的问题 它问了几乎相同的问题 但又不完全一样 我的问题是如何将 a gt Bool 类型的函数列表组合成一个也是 a gt Bool 的函数 Ex compose a gt Bool gt a gt Bool comp
  • 带边界的 haskell 列表数据类型

    我有以下类型定义来表示卡片 data Suit Hearts Spades Diamonds Clubs data Rank Numeric Integer Jack Queen King Ace data Card Card Rank S
  • 当您包含导入 Gloss 的项目时,“stack ghci”会失败

    如果您在 Stack 项目中导入 Gloss 并使用stack ghci 您会收到以下错误 GHCi version 7 10 2 http www haskell org ghc for help

随机推荐