如何编写一个在列表元素之间执行逻辑和函数的函数?
我写了这个:
iand :: [IO Bool] -> IO Bool
iand [] = return (True)
iand (x:xs) = do
a <- x
b <- iand(xs)
return (a && b)
但这似乎是不自觉的。
如何用 foldM (liftM) 重写这个函数?
谢谢你。
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
我切换到liftM
fromfmap
是因为,虽然fmap
' 在某种意义上“更好”,但它会施加额外的Functor m
约束。这应该不是问题,但可能是出于历史原因,所以我谨慎行事。
另外,您可能会问“我怎么会知道sequence
”?答案是美妙的Hoogle,它允许您按名称或类型搜索 Haskell 函数。因此,既然您知道liftM :: Monad m => (a -> b) -> m a -> m b
,您可能已经意识到您需要类似的东西Monad m => [m a] -> m [a]
;确实出现了sequence
。
1:或者,至少,它们应该是——但由于历史原因,情况并非总是如此。
您可以使用liftM
将and
(具有 type [Bool] -> Bool
)转换为类型的函数IO [Bool] -> IO Bool
并将sequence
您的[IO Bool]
转换为IO [Bool]
.
所以你的功能变成:
iand ibs = liftM and (sequence ibs)
这不是iand list = foldl (&&) True list
吗?