我看到很多功能是根據模式(f .) . g
定義的。例如:什麼是(f。)。 g的意思是在Haskell中?
countWhere = (length .) . filter
duplicate = (concat .) . replicate
concatMap = (concat .) . map
這是什麼意思?
我看到很多功能是根據模式(f .) . g
定義的。例如:什麼是(f。)。 g的意思是在Haskell中?
countWhere = (length .) . filter
duplicate = (concat .) . replicate
concatMap = (concat .) . map
這是什麼意思?
點運算符(即(.)
)是function composition運算符。它被定義爲如下:
infixr 9 .
(.) :: (b -> c) -> (a -> b) -> a -> c
f . g = \x -> f (g x)
正如你可以看到它需要b -> c
類型的函數,並且a -> b
類型的另一個函數,並返回a -> c
類型的函數(即,所述第二函數的結果適用於第一功能)。
函數組合運算符非常有用。它允許您將一個函數的輸出傳遞給另一個函數的輸入。
main = interact (\x -> unlines (reverse (lines x)))
不是很易讀:舉例如下,你可以寫在Haskell一個tac程序。使用功能組成但是你如下可以寫它:
main = interact (unlines . reverse . lines)
正如你所看到的函數組合是非常有用的,但你不能在任何地方使用它。例如,你不能管的filter
輸出到length
使用函數組合:
countWhere = length . filter -- this is not allowed
這是不允許的原因是因爲filter
是(a -> Bool) -> [a] -> [a]
類型。將其與a -> b
進行比較,我們發現a
的類型爲(a -> Bool)
,而b
的類型爲[a] -> [a]
。這導致類型不匹配,因爲Haskell預計length
的類型爲b -> c
(即([a] -> [a]) -> c
)。但它實際上是[a] -> Int
類型。
的解決方案是非常簡單的:
countWhere f = length . filter f
但是有些人不喜歡額外的懸掛f
。他們喜歡寫countWhere
在pointfree風格如下:
countWhere = (length .) . filter
他們如何獲得呢?試想一下:
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
正如你可以看到(f .) . g
簡直是\x y -> f (g x y)
。這個概念實際上可以重複:
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)
這不是很漂亮,但它完成了工作。鑑於兩個功能,你還可以編寫自己的函數組合運營商:
f .: g = (f .) . g
f .:: g = ((f .) .) . g
f .::: g = (((f .) .) .) . g
使用(.:)
操作符,你可以寫countWhere
爲代替如下:有趣的是
countWhere = length .: filter
儘管你可以在點自由風格寫(.:)
好:
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:
(.:) = (.) . (.)
同樣,我們得到:
(.::) = (.) . (.) . (.)
(.:::) = (.) . (.) . (.) . (.)
正如你可以看到(.:)
,(.::)
和(.:::)
都只是(.)
權力(即他們是的(.)
)。對於數字數學:
x^0 = 1
x^n = x * x^(n - 1)
同樣的功能在數學:
f .^ 0 = id
f .^ n = f . (f .^ (n - 1))
如果f
是(.)
則:
(.) .^ 1 = (.)
(.) .^ 2 = (.:)
(.) .^ 3 = (.::)
(.) .^ 4 = (.:::)
這使我們接近這個文章的末尾。對於最後的挑戰,讓我們寫pointfree風格以下功能:
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
我們可以進一步簡化這個如下:
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 (.))
使用compose
你現在可以寫mf
爲:
mf = compose map filter
是的,它有點醜,但它也是一個非常令人敬畏的令人難以置信的概念。您現在可以編寫\x y z -> f x (g y z)
的任何函數作爲compose f g
,這非常整潔。
表達式'(。)^ i'不是很好的類型,所以它們實際上並不是有效的Haskell。 –
是的。然而,我確實寫了「對數學中的函數類似」,因爲這是一個數學解釋,我認爲使用'^'代替數字函數是可以的。不過,我會將操作符更改爲'。^'來區分這兩者。 –
我也很驚訝地發現數學中還有(。)^ i'。也許這種事物的正式框架存在於依賴型理論中。這將是有趣的。 –
這是一個味道的問題,但我覺得這樣的風格是不愉快的。首先我會描述它的意思,然後我建議一個我更喜歡的替代方案。
對於任何運營商?
,您需要知道(f . g) x = f (g 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) x y = f (g x y)
然後countWhere = length .: filter
。我個人覺得這更清晰。
(.:
在Data.Composition
,可能其他地方也定義)
您也可以將'(。:)'定義爲'(。:) = fmap fmap fmap'。它更通用,因爲你可以將它用於任何函子。例如你可以做'(* 2)。:只是[1..5]'。當然,你需要給它正確的類型簽名'(。:) :) ::(Functor f,Functor g)=>(a - > b) - > f(g a) - > f(g b)'。 –
@AaditMShah在這種情況下,我寧願像'<$$> = fmap。因爲'(。:)'按照慣例專門用於'( - >)r',而「外部」'fmap'則在'( - >)r'仿函數上。 – kqr
(F)。g也可能是原作者代碼中很好僞裝的觀點的一部分。 – Marton
我不確定這意味着什麼。 –
這意味着作者正在聰明,並最終編寫不可讀的代碼。 ;) – tibbe