为什么这个函数的pointfree版本看起来像这样?

时间:2010-06-26 11:53:10

标签: haskell pointfree

我一直在玩Haskell,包括练习以无点形式编写函数。这是一个示例函数:

dotProduct :: (Num a) => [a] -> [a] -> a
dotProduct xs ys = sum (zipWith (*) xs ys)

我想以无点形式编写此函数。这是我在其他地方找到的一个例子:

dotProduct = (sum .) . zipWith (*)

但是,我不明白为什么无点表单看起来像(sum .) . zipWith (*)而不是sum . zipWith (*)。为什么括号中的和有2个合成运算符?

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 gf给出一个参数的结果适用g
  • (.) (.) (.) f gf给出两个参数的结果适用g
  • (.) (.) ((.) (.) (.)) f gf给出三个参数的结果适用g
  • ...
  • (.~) = (.) (.) (.)(.~~) = (.) (.) (.~)(.~~~) = (.) (.) (.~~),现在let foo a b c d = [1..5]; (.~~~) sum foo 0 0 0 0可以15
    • 但我不会这样做。它可能会使代码无法读取。只是点了点。
  • Conal的TypeCompose提供了名为(.)的{​​{1}}的同义词。也许这个名字对于了解正在发生的事情更有帮助。
    • result也可以代替fmap,如果导入相关的实例((.)会这样做),但它的类型更通用,因此可能更令人困惑。
  • Conal的“融合”概念(不要与“融合”的其他用法相混淆)是一种相关的,imho提供了一种很好的组合功能的方法。 this long Google Tech Talk that Conal gave
  • 中的更多详细信息