13

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

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

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

dotProduct = (sum .) . zipWith (*)

但是,我不明白为什么无点形式看起来像(sum .) . zipWith (*)而不是sum . zipWith (*). 为什么 sum 在括号中并且有 2 个组合运算符?

4

2 回答 2

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].

于 2010-06-26T11:59:15.923 回答
13

KennyTM 的回答非常好,但我仍然想提供另一个观点:

dotProduct = (.) (.) (.) sum (zipWith (*))
  • (.) f g适用fg给定一个参数的结果
  • (.) (.) (.) f g适用fg给定两个参数的结果
  • (.) (.) ((.) (.) (.)) f g适用fg给定三个参数的结果
  • ...
  • 可以 做(.~) = (.) (.) (.),,现在结果(.~~) = (.) (.) (.~)(.~~~) = (.) (.) (.~~)let foo a b c d = [1..5]; (.~~~) sum foo 0 0 0 015
    • 但我不会这样做。它可能会使代码不可读。只要点满。
  • ConalTypeCompose提供了(.)called的同义词result。也许这个名字更有助于理解正在发生的事情。
    • fmap(.)如果导入相关实例(import Control.Applicative会这样做) ,也可以代替,但它的类型更通用,因此可能更令人困惑。
  • Conal 的“融合”概念(不要与“融合”的其他用法混淆)有点相关,恕我直言,提供了一种组合函数的好方法。Conal 提供的这个冗长的 Google 技术讲座中的更多详细信息
于 2010-06-26T12:35:06.173 回答