1

如何编写一个在列表元素之间执行逻辑和函数的函数?

我写了这个:

iand :: [IO Bool] -> IO Bool

iand [] = return (True)
iand (x:xs) = do
  a <- x 
  b <- iand(xs)
  return (a && b)

但这似乎是不自觉的。

如何用 foldM (liftM) 重写这个函数?

谢谢你。

4

3 回答 3

10

Prelude 函数and :: [Bool] -> Bool几乎可以满足您的需求,但它不是一元的。一般来说,要将单参数函数提升为 monad,您需要 Control.Monad 的liftM :: Monad m => (a -> b) -> m a -> b a; 但是,更一般地说,您可以使用 Prelude 的fmap :: Functor f => (a -> b) -> f a -> f b. 所有单子都是函子1,所以这没关系。因此,您可以使用

fand' :: Functor f => f [Bool] -> f Bool
fand' = fmap and

但是,至少在 90% 的情况下,我会使用 Control.Applicative 的同义词将内联写为fmap and xs,或者更可能的是。and <$> xs<$>fmap

当然,我相信你已经注意到了,这不是你想要的。为此,您需要 Prelude 的sequence :: Monad m => [m a] -> m [a]. 你现在有一个 function[m a] -> m [a]和一个 function f [Bool] -> f Bool,所以我们可以将它们组合起来:

mand :: Monad m => [m Bool] -> m Bool
mand = liftM and . sequence

我切换到liftMfromfmap是因为,虽然fmap' 在某种意义上“更好”,但它会施加额外的Functor m约束。这应该不是问题,但可能是出于历史原因,所以我谨慎行事。

另外,您可能会问“我怎么会知道sequence”?答案是美妙的Hoogle,它允许您按名称或类型搜索 Haskell 函数。因此,既然您知道liftM :: Monad m => (a -> b) -> m a -> m b,您可能已经意识到您需要类似的东西Monad m => [m a] -> m [a];确实出现了sequence


1:或者,至少,它们应该是——但由于历史原因,情况并非总是如此。

于 2010-12-11T23:15:18.333 回答
2

您可以使用liftMand(具有 type [Bool] -> Bool)转换为类型的函数IO [Bool] -> IO Bool并将sequence您的[IO Bool]转换为IO [Bool].

所以你的功能变成:

iand ibs = liftM and (sequence ibs)
于 2010-12-11T23:00:32.457 回答
1

这不是iand list = foldl (&&) True list吗?

于 2010-12-11T22:56:17.647 回答