[英]What does (f .) . g mean in Haskell?
I have seen a lot of functions being defined according to the pattern (f .) . g
我已经看到很多函数是根据模式定义的(f .) . g
(f .) . g
. (f .) . g
。 For example: 例如:
countWhere = (length .) . filter
duplicate = (concat .) . replicate
concatMap = (concat .) . map
What does this mean? 这是什么意思?
The dot operator (ie (.)
) is the function composition operator. 点运算符(即(.)
)是函数组合运算符。 It is defined as follows: 它的定义如下:
infixr 9 .
(.) :: (b -> c) -> (a -> b) -> a -> c
f . g = \x -> f (g x)
As you can see it takes a function of type b -> c
and another function of type a -> b
and returns a function of type a -> c
(ie which applies the first function to the result of the second function). 正如您所看到的,它需要类型为b -> c
函数和类型为a -> b
另一个函数,并返回类型为a -> c
的函数(即将第一个函数应用于第二个函数的结果)。
The function composition operator is very useful. 函数组合运算符非常有用。 It allows you to pipe the output of one function into the input of another function. 它允许您将一个函数的输出传递给另一个函数的输入。 For example you could write a tac program in Haskell as follows: 例如,你可以在Haskell中编写一个tac程序,如下所示:
main = interact (\x -> unlines (reverse (lines x)))
Not very readable. 不太可读。 Using function composition however you could write it as follows: 但是使用函数组合,您可以按如下方式编写它:
main = interact (unlines . reverse . lines)
As you can see function composition is very useful but you can't use it everywhere. 正如您所看到的,功能组合非常有用,但您无法在任何地方使用它。 For example you can't pipe the output of filter
into length
using function composition: 例如,您无法使用函数组合将filter
的输出filter
给length
:
countWhere = length . filter -- this is not allowed
The reason this is not allowed is because filter
is of type (a -> Bool) -> [a] -> [a]
. 不允许这样做的原因是因为filter
的类型是(a -> Bool) -> [a] -> [a]
。 Comparing it with a -> b
we find that a
is of type (a -> Bool)
and b
is of type [a] -> [a]
. 将它与a -> b
进行比较,我们发现a
的类型为(a -> Bool)
, b
的类型为[a] -> [a]
。 This results in a type mismatch because Haskell expects length
to be of type b -> c
(ie ([a] -> [a]) -> c
). 这导致类型不匹配,因为Haskell期望length
为b -> c
类型(即([a] -> [a]) -> c
)。 However it's actually of type [a] -> Int
. 然而它实际上是[a] -> Int
。
The solution is pretty simple: 解决方案非常简单:
countWhere f = length . filter f
However some people don't like that extra dangling f
. 然而,有些人不喜欢那个额外的悬挂f
。 They prefer to write countWhere
in pointfree style as follows: 他们更喜欢以countWhere
点样式编写countWhere
,如下所示:
countWhere = (length .) . filter
How do they get this? 他们怎么得到这个? Consider: 考虑:
countWhere f xs = length (filter f xs)
-- But `f x y` is `(f x) y`. Hence:
countWhere f xs = length ((filter f) xs)
-- But `\x -> f (g x)` is `f . g`. Hence:
countWhere f = length . (filter f)
-- But `f . g` is `(f .) g`. Hence:
countWhere f = (length .) (filter f)
-- But `\x -> f (g x)` is `f . g`. Hence:
countWhere = (length .) . filter
As you can see (f .) . g
如你所见(f .) . g
(f .) . g
is simply \\xy -> f (gxy)
. (f .) . g
只是\\xy -> f (gxy)
。 This concept can actually be iterated: 这个概念实际上可以迭代:
f . g --> \x -> f (g x)
(f .) . g --> \x y -> f (g x y)
((f .) .) . g --> \x y z -> f (g x y z)
(((f .) .) .) . g --> \w x y z -> f (g w x y z)
It's not pretty but it gets the job done. 它并不漂亮,但它完成了工作。 Given two functions you can also write your own function composition operators: 给定两个函数,您还可以编写自己的函数组合运算符:
f .: g = (f .) . g
f .:: g = ((f .) .) . g
f .::: g = (((f .) .) .) . g
Using the (.:)
operator you could write countWhere
as follows instead: 使用(.:)
运算符,您可以编写countWhere
,如下所示:
countWhere = length .: filter
Interestingly though you could write (.:)
in point free style as well: 有趣的是,你也可以用点自由风格编写(.:)
:
f .: g = (f .) . g
-- But `f . g` is `(.) f g`. Hence:
f .: g = (.) (f .) g
-- But `\x -> f x` is `f`. Hence:
(f .:) = (.) (f .)
-- But `(f .)` is `((.) f)`. Hence:
(f .:) = (.) ((.) f)
-- But `\x -> f (g x)` is `f . g`. Hence:
(.:) = (.) . (.)
Similarly we get: 同样我们得到:
(.::) = (.) . (.) . (.)
(.:::) = (.) . (.) . (.) . (.)
As you can see (.:)
, (.::)
and (.:::)
are just powers of (.)
(ie they are iterated functions of (.)
). 正如你所看到的那样(.:)
, (.::)
和(.:::)
只是(.)
幂(即它们是(.)
迭代函数 )。 For numbers in Mathematics: 对于数学中的数字:
x ^ 0 = 1
x ^ n = x * x ^ (n - 1)
Similarly for functions in Mathematics: 类似于数学中的函数:
f .^ 0 = id
f .^ n = f . (f .^ (n - 1))
If f
is (.)
then: 如果f
是(.)
那么:
(.) .^ 1 = (.)
(.) .^ 2 = (.:)
(.) .^ 3 = (.::)
(.) .^ 4 = (.:::)
That brings us close to the end of this article. 这使我们接近本文的结尾。 For a final challenge let's write the following function in pointfree style: 对于最后的挑战,让我们以无点样式编写以下函数:
mf a b c = filter a (map b c)
mf a b c = filter a ((map b) c)
mf a b = filter a . (map b)
mf a b = (filter a .) (map b)
mf a = (filter a .) . map
mf a = (. map) (filter a .)
mf a = (. map) ((filter a) .)
mf a = (. map) ((.) (filter a))
mf a = ((. map) . (.)) (filter a)
mf = ((. map) . (.)) . filter
mf = (. map) . (.) . filter
We can further simplify this as follows: 我们可以进一步简化如下:
compose f g = (. f) . (.) . g
compose f g = ((. f) . (.)) . g
compose f g = (.) ((. f) . (.)) g
compose f = (.) ((. f) . (.))
compose f = (.) ((. (.)) (. f))
compose f = ((.) . (. (.))) (. f)
compose f = ((.) . (. (.))) (flip (.) f)
compose f = ((.) . (. (.))) ((flip (.)) f)
compose = ((.) . (. (.))) . (flip (.))
Using compose
you can now write mf
as: 使用compose
您现在可以将mf
写为:
mf = compose map filter
Yes it is a bit ugly but it's also a really awesome mind-boggling concept. 是的它有点难看,但它也是一个令人难以置信的令人难以置信的概念。 You can now write any function of the form \\xyz -> fx (gyz)
as compose fg
and that is very neat. 你现在可以编写形式\\xyz -> fx (gyz)
任何函数作为compose fg
,这非常简洁。
This is a matter of taste, but I find such style to be unpleasant. 这是一个品味问题,但我发现这种风格令人不愉快。 First I'll describe what it means, and then I suggest an alternative that I prefer. 首先,我将描述它的意义,然后我建议我更喜欢的替代方案。
You need to know that (f . g) x = f (gx)
and (f ?) x = f ? x
你需要知道(f . g) x = f (gx)
和(f ?) x = f ? x
(f ?) x = f ? x
for any operator ?
(f ?) x = f ? x
对于任何运营商?
. 。 From this we can deduce that 由此我们可以推断出这一点
countWhere p = ((length .) . filter) p
= (length .) (filter p)
= length . filter p
so 所以
countWhere p xs = length (filter p xs)
I prefer to use a function called .:
我更喜欢使用一个名为.:
的函数.:
(.:) :: (r -> z) -> (a -> b -> r) -> a -> b -> z
(f .: g) x y = f (g x y)
Then countWhere = length .: filter
. 然后countWhere = length .: filter
。 Personally I find this a lot clearer. 就个人而言,我发现这一点更加清晰。
( .:
is defined in Data.Composition
and probably other places too.) ( .:
在Data.Composition
定义,也可能在其他地方定义。)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.