如何组合 return Bools 为一个函数的函数

How to compose functions that return Bools to one function

我在这里发现了一个类似的问题,它问的几乎是同样的事情,但又不完全是。

我的问题是如何将类型为 (a -> Bool) 的函数列表组合成一个也是 (a -> Bool) 的函数。

例如

compose :: [(a -> Bool)] -> (a -> Bool)
compose []     = **?**
compose (x:xs) = x **?** compose xs

与此类似的问题是采用三个函数并将它们全部混合在一起:

newFunction x f g y = f x || g x || y x

但这非常有限,因为您必须提供特定数量的函数,而且它不会 return 另一个函数,它 return 是一个布尔值。我本质上想要一个函数,它能给我上面的函数而不用函数作为参数。

我尝试使用 Monoid 来完成这项工作,但我 运行 遇到了首先将函数包装到 Monoid 中的问题,更不用说将它们组合在一起作为 newFunction 确实如此。

有没有办法将类型为 (a -> Bool) 的函数列表组合成一个相同类型的函数?

我们可以在这里利用any :: Foldable => (a -> Bool) -> f a -> Bool

compose :: Foldable f => f (a -> Bool) -> a -> Bool
compose = flip (any . flip ($))

或如 所建议的那样,使用 (&):

import Data.Function((&))

compose :: Foldable f => f (a -> Bool) -> a -> Bool
compose = flip (any . (&))

或者没有点自由样式(并且可能更容易理解):

compose :: Foldable f => f (a -> Bool) -> a -> Bool
compose l x = any ($ x) l

以上内容适用于任何类型的 Foldable,因此列表 []Maybe

一个更简单的例子(我不是 Haskeller),根据您的要求:

compose :: [(a -> Bool)] -> (a -> Bool)
compose []     = (\y -> False)
compose (x:xs)  = (\y -> (x y) || ((compose xs) y))

看:compose xs在你的定义中是一个函数。所以你可以用一个参数来调用它 - 比如 compose xs a, - 那将 return a Bool.

您可以使用它来定义递归情况。

首先,递归案例必须 return 一个函数 - 因为这是您的类型签名声明的内容。所以它必须看起来像:

compose (x:xs) = \a -> ...

现在,逻辑是这样的:首先,调用列表中的第一个函数 - 如 x a, - 如果它 return 为真,那么这就是结果;否则,调用尾部的组合 - 如 compose xs a。让我们记下来:

compose (x:xs) = \a -> x a || compose xs a

接下来,您需要决定如何处理空列表。显然,它可以是一个总是 returns True 的函数,也可以是一个总是 returns False 的函数,除非你能以某种方式检查参数,否则没有其他选择,你不能,因为它是通用类型。

所以,它应该return True 还是False?让我们看看:如果它 returns True,那么任何组合将永远是 True,这就是 || 运算符的工作原理。所以我们不妨直接写成compose _ = \_ -> True。因此,唯一合理的变体是 return False.

总结以上所有内容,这是您的定义:

compose [] = \a -> False
compose (x:xs) = \a -> x a || compose xs a

当然,您可以使用更短的语法而不是 returning lambda:

compose [] a = False
compose (x:xs) a = x a || compose xs a

要使用幺半群实现此功能,您可以使用 Any(来自 Data.Monoid)布尔包装器,它在组合值时实现您想要的析取行为,例如

(Any False) `mappend` (Any True)
=> Any {getAny = True}

return 幺半群值的函数本身就是幺半群 - mappend 两个这样的函数 return 一个函数计算两个函数的参数和 mappend 结果例如

f :: Int -> Any
f x = Any $ x > 10

g :: Int -> Any
g x = Any $ x < 3

comp :: Int -> Any
comp = f `mappend` g

comp 0
=> Any {getAny = True}

comp 4
=> Any {getAny = False}

comp 11
=> Any {getAny = True}

因此,如果您将每个 a -> Bool 提升到一个函数 a -> Any 中,那么它们将由 mappend.

组成

mconcat 将幺半群值列表缩减为单个值,因此将其应用于 a -> Any 函数列表 return 是一个将析取应用于每个结果的函数。然后,您需要使用 getAny.

从结果 Any 值中解包 Bool
import Data.Monoid

compose :: [(a -> Bool)] -> (a -> Bool)
compose fs x = let anyfs = map (\f -> Any . f) fs
                   combined = mconcat anyfs
                   anyResult = combined x
                in getAny anyResult

这也可以写成:

compose :: [(a -> Bool)] -> (a -> Bool)
compose = (getAny .) . mconcat . (map (Any .))

正如 danidiaz 在评论中指出的,您也可以使用 foldMap。这还有一个更通用的类型:

compose :: Foldable t => t (a -> Bool) -> a -> Bool
compose = (getAny .) . foldMap (Any .)