Haskell中的高阶OR

时间:2014-05-19 10:31:32

标签: haskell

以下是我想到的一个示例问题: 取每个x从1到n的总和,其中x可以被3或5整除,所以 像这样的东西:

divisible a b = rem b a == 0
sum3or5 n = sum [x | x <- [1..n], divisible 3 x || divisible 5 x]

来自Scheme,我想使用过滤器实现这一点,如下所示:

divisible a b = rem b a == 0
sum3or5 n = sum $ filter div3or5 [1..n] where
    div3or5 n = (divides 3 n) || (divides 5 n)

我在想,是否有一个更高阶的逻辑OR(||),这样我就可以编写&#39; div3or5&#39;无点样式,这样的东西?:

divisible a b = rem a b == 0
sum3or5 = sum $ filter (divisible 3 || divisible 5) . range

感谢您的帮助。

2 个答案:

答案 0 :(得分:11)

是。你可以将(||)从布尔“提升”到功能,从某种东西到布尔。所以你想要像

这样的东西
(||) :: Bool -> Bool -> Bool

变成

(||) :: (r -> Bool) -> (r -> Bool) -> (r -> Bool)

这恰好是函数的应用实例的好处。

liftA2      :: (a -> b -> c) -> (r -> a) -> (r -> b) -> (r -> c)

所以

liftA2 (||) :: (r -> Bool) -> (r -> Bool) -> (r -> Bool)

这意味着,在您的情况下,您可以将过滤器编写为

filter (liftA2 (||) (divides 3) (divides 5))

取一个整数,并判断它是否可以被3或5整除。


如果需要,您可以定义类似

的内容
(<||>) = liftA2 (||)

或等同地

f <||> g = \x -> f x || g x

然后您可以将过滤器编写为

filter (divisible 3 <||> divisible 5)

在操作符周围包装尖括号是一种习惯用法,表明它们被提升为其他东西(仿函数,应用程序,幺半群)。

答案 1 :(得分:3)

Control.Arrow模块将有所帮助。

import Control.Arrow

divisible a b = rem a b == 0
range a = [1..a]
sum3or5 = sum . filter (divisible 3 .||. divisible 5) . range
a .||. b = (uncurry (||)) . (a &&& b)

(&&&)是扇入式运算符,类型为Arrow a => a b c -> a b c' -> a b (c, c'),在这种情况下,我们使用(->)作为箭头。这会给我们一个函数b -> (Bool,Bool),然后我们可以用一个未经验证的(||)或任何其他布尔运算符来组合它。