Haskell - 这个平均数函数是如何工作的?

9

我发现了这个求平均函数的实现:

avg :: [Int] -> Int
avg = div . sum <*> length

这是怎么工作的?我查看了由 div . sum 生成的函数:
(div . sum) :: (Integral a, Foldable t) => t a -> a -> a

我理解您的问题,但我无法确定 <*> length 是如何工作的。
2个回答

12

<*> :: 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


4
借鉴这个答案,帮助我理解在函数中滥用 <*> 的一种方法是把它读作 div <$> sum <*> length。如果你熟悉 f <$> x <*> y <*> z ... 的惯用法,你可以把函数的情况看作是将“将 x 应用于 f 的第一个参数,y 应用于第二个参数,以此类推”。 - cole

9

我不是特别喜欢这个所谓的pointfree技巧。它使用了Applicative (a->)实例作为“扇出”,将参数传递给两个单独的函数。本质上,这两个函数是sumlength,然后通过div组合回来,这可以用箭头组合器很好地表达(尽管有点冗长,因为箭头并不完全符合Haskell的默认柯里化风格):

import Control.Arrow

avg = uncurry div . (sum &&& length)

在应用程序技巧中,您将结合函数合并到第一个参数共享函数中。因此,在这种情况下是 div . sum,另一个函数 length 的结果将被传递到第一个函数的第二个参数中。

您还可以使用

avg = liftA2 div sum length

它还使用了 Applicative 实例。


没错!实际上我一开始就是这样做的,但是后来在阅读最后的答案时不知怎么地犯了一个思维错误,认为整个东西仍然有两个参数。 - leftaroundabout

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接