我发现了这个求平均函数的实现:
avg :: [Int] -> Int
avg = div . sum <*> length
这是怎么工作的?我查看了由
div . sum
生成的函数:(div . sum) :: (Integral a, Foldable t) => t a -> a -> a
我理解您的问题,但我无法确定
<*> length
是如何工作的。我发现了这个求平均函数的实现:
avg :: [Int] -> Int
avg = div . sum <*> length
div . sum
生成的函数:(div . sum) :: (Integral a, Foldable t) => t a -> a -> a
<*> length
是如何工作的。<*> :: Applicative f => f (a -> b) -> f a -> f b
是作用在Applicative
结构上的顺序应用函数。对于一个函数来说,这相当于[src]:
所以
instance Applicative ((->) r) where pure = const <b>(<*>) f g x = f x (g x)</b> liftA2 q f g x = q (f x) (g x)
f <*> g
是\x -> f x (g x)
的简写。因此在avg
的情况下:avg = div . sum <*> length
等价于:
avg x = (div . sum) x (length x)
这与以下内容等效:
avg x = div (sum x) (length x)
所以它将
sum x
除以length x
。
我不是特别喜欢这个所谓的pointfree技巧。它使用了Applicative (a->)
实例作为“扇出”,将参数传递给两个单独的函数。本质上,这两个函数是sum
和length
,然后通过div
组合回来,这可以用箭头组合器很好地表达(尽管有点冗长,因为箭头并不完全符合Haskell的默认柯里化风格):
import Control.Arrow
avg = uncurry div . (sum &&& length)
在应用程序技巧中,您将结合函数合并到第一个参数共享函数中。因此,在这种情况下是 div . sum
,另一个函数 length
的结果将被传递到第一个函数的第二个参数中。
您还可以使用
avg = liftA2 div sum length
它还使用了 Applicative
实例。
<*>
的一种方法是把它读作div <$> sum <*> length
。如果你熟悉f <$> x <*> y <*> z ...
的惯用法,你可以把函数的情况看作是将“将x
应用于f
的第一个参数,y
应用于第二个参数,以此类推”。 - cole