我一直在玩Haskell,包括练习以无点形式编写函数。这是一个示例函数:
dotProduct :: (Num a) => [a] -> [a] -> a
dotProduct xs ys = sum (zipWith (*) xs ys)
我想以无点形式编写此函数。这是我在其他地方找到的一个例子:
dotProduct = (sum .) . zipWith (*)
但是,我不明白为什么无点表单看起来像(sum .) . zipWith (*)
而不是sum . zipWith (*)
。为什么括号中的和有2个合成运算符?
答案 0 :(得分:19)
dotProduct xs ys = sum (zipWith (*) xs ys) -- # definition
dotProduct xs = \ys -> sum (zipWith (*) xs ys) -- # f x = g <=> f = \x -> g
= \ys -> (sum . (zipWith (*) xs)) ys -- # f (g x) == (f . g) x
= sum . (zipWith (*) xs) -- # \x -> f x == f
= sum . zipWith (*) xs -- # Precedence rule
dotProduct = \xs -> sum . zipWith (*) xs -- # f x = g <=> f = \x -> g
= \xs -> (sum .) (zipWith (*) xs) -- # f * g == (f *) g
= \xs -> ((sum .) . zipWith (*)) xs -- # f (g x) == (f . g) x
= (sum .) . zipWith (*) -- # \x -> f x == f
(sum .)
是一个部分。它被定义为
(sum .) f = sum . f
任何二元运算符都可以这样写,例如map (7 -) [1,2,3] == [7-1, 7-2, 7-3]
。
答案 1 :(得分:13)
KennyTM的答案非常好,但我还想提出另一个观点:
dotProduct = (.) (.) (.) sum (zipWith (*))
(.) f g
对f
给出一个参数的结果适用g
(.) (.) (.) f g
对f
给出两个参数的结果适用g
(.) (.) ((.) (.) (.)) f g
对f
给出三个参数的结果适用g
(.~) = (.) (.) (.)
,(.~~) = (.) (.) (.~)
,(.~~~) = (.) (.) (.~~)
,现在let foo a b c d = [1..5]; (.~~~) sum foo 0 0 0 0
可以15
。
TypeCompose
提供了名为(.)
的{{1}}的同义词。也许这个名字对于了解正在发生的事情更有帮助。
result
也可以代替fmap
,如果导入相关的实例((.)
会这样做),但它的类型更通用,因此可能更令人困惑。