什么(f。)。 g的意思是在Haskell?
我已经看到很多function是根据模式(f .) . g
(f .) . g
。 例如:
countWhere = (length .) . filter duplicate = (concat .) . replicate concatMap = (concat .) . map
这是什么意思?
点运算符(即(.)
)是函数组合运算符。 它被定义如下:
infixr 9 . (.) :: (b -> c) -> (a -> b) -> a -> c f . g = \x -> f (gx)
正如你所看到的,它需要一个types为b -> c
函数和另一个types为a -> b
的函数,并返回一个typesa -> c
的函数(即将第二个函数的结果应用于第一个函数)。
函数组合运算符非常有用。 它允许您将一个函数的输出传递给另一个函数的input。 例如,你可以在Haskell中编写一个tac程序,如下所示:
main = interact (\x -> unlines (reverse (lines x)))
不太可读。 使用函数组合,但是你可以写如下:
main = interact (unlines . reverse . lines)
正如你所看到的function组合是非常有用的,但你不能在任何地方使用它。 例如,你不能使用函数组合将pipe道的输出变成length
:
countWhere = length . filter -- this is not allowed
这是不允许的原因是因为filter
types(a -> Bool) -> [a] -> [a]
。 将其与a -> b
进行比较,我们发现a
是(a -> Bool)
, b
是[a] -> [a]
。 这会导致types不匹配,因为Haskell期望length
为b -> c
(即([a] -> [a]) -> c
)。 但是它实际上是types[a] -> Int
。
解决scheme非常简单:
countWhere f = length . filter f
但是有些人不喜欢那个额外的悬挂f
。 他们更喜欢在以下几点写下countWhere
:
countWhere = (length .) . filter
他们如何得到这个? 考虑:
countWhere f xs = length (filter f xs) -- But `fxy` is `(fx) y`. Hence: countWhere f xs = length ((filter f) xs) -- But `\x -> f (gx)` is `f . g`. Hence: countWhere f = length . (filter f) -- But `f . g` is `(f .) g`. Hence: countWhere f = (length .) (filter f) -- But `\x -> f (gx)` is `f . g`. Hence: countWhere = (length .) . filter
正如你所见(f .) . g
(f .) . g
就是\xy -> f (gxy)
。 这个概念实际上可以迭代:
f . g --> \x -> f (gx) (f .) . g --> \xy -> f (gxy) ((f .) .) . g --> \xyz -> f (gxyz) (((f .) .) .) . g --> \wxyz -> f (gwxyz)
这不是很好,但它完成了工作。 给定两个函数,你也可以编写自己的函数组合操作符:
f .: g = (f .) . g f .:: g = ((f .) .) . g f .::: g = (((f .) .) .) . g
使用(.:)
运算符,您可以按如下所示编写countWhere
:
countWhere = length .: filter
有趣的是,尽pipe你也可以用点自由风格来写(.:)
:
f .: g = (f .) . g -- But `f . g` is `(.) fg`. Hence: f .: g = (.) (f .) g -- But `\x -> fx` is `f`. Hence: (f .:) = (.) (f .) -- But `(f .)` is `((.) f)`. Hence: (f .:) = (.) ((.) f) -- But `\x -> f (gx)` is `f . g`. Hence: (.:) = (.) . (.)
同样我们得到:
(.::) = (.) . (.) . (.) (.:::) = (.) . (.) . (.) . (.)
正如你所看到的(.:)
, (.::)
和(.:::)
只是(.)
幂(即它们是(.)
迭代函数 )。 对于math中的数字:
x ^ 0 = 1 x ^ n = x * x ^ (n - 1)
类似于math中的函数:
f .^ 0 = id f .^ n = f . (f .^ (n - 1))
如果f
是(.)
那么:
(.) .^ 1 = (.) (.) .^ 2 = (.:) (.) .^ 3 = (.::) (.) .^ 4 = (.:::)
这使我们接近本文的结尾。 对于最后的挑战,让我们用无点点风格写下面的函数:
mf abc = filter a (map bc) mf abc = filter a ((map b) c) mf ab = filter a . (map b) mf ab = (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
我们可以进一步简化如下:
compose fg = (. f) . (.) . g compose fg = ((. f) . (.)) . g compose fg = (.) ((. f) . (.)) g compose f = (.) ((. f) . (.)) compose f = (.) ((. (.)) (. f)) compose f = ((.) . (. (.))) (. f) compose f = ((.) . (. (.))) (flip (.) f) compose f = ((.) . (. (.))) ((flip (.)) f) compose = ((.) . (. (.))) . (flip (.))
使用compose
你现在可以写mf
为:
mf = compose map filter
是的,这有点难看,但这也是一个令人难以置信的令人难以置信的概念。 你现在可以编写\xyz -> fx (gyz)
这个forms的任何函数作为compose fg
,而且非常整洁。
这是一个味道的问题,但我觉得这样的风格是不愉快的。 首先我要描述它的意思,然后我build议一个我更喜欢的替代scheme。
你需要知道(f . g) x = f (gx)
和(f ?) x = f ? x
(f ?) x = f ? x
对于任何运营商?
。 由此我们可以推断出这一点
countWhere p = ((length .) . filter) p = (length .) (filter p) = length . filter p
所以
countWhere p xs = length (filter p xs)
我更喜欢使用一个名为.:
的函数.:
(.:) :: (r -> z) -> (a -> b -> r) -> a -> b -> z (f .: g) xy = f (gxy)
然后countWhere = length .: filter
。 我个人觉得这更清楚。
( .:
在Data.Composition
定义,也可能在其他地方。)