以下是我想到的一个示例问题: 取每个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
感谢您的帮助。
答案 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)
,然后我们可以用一个未经验证的(||)
或任何其他布尔运算符来组合它。