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

14

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

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

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

dotProduct = (sum .) . zipWith (*)

然而,我不理解为什么点无关形式看起来像 (sum .) . zipWith (*) 而不是 sum . zipWith (*)。为什么 sum 要加括号并且有两个组合运算符?

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 .)是一个section。它的定义如下:

(sum .) f = sum . f

任何二元操作符都可以像这样写,例如:map (7 -) [1,2,3] == [7-1,7-2,7-3]


在这个表达式f * g == (f *) g中的 * 是否和.函数组合是一样的意思? - guhou
@Bleu:是的,任何二元运算符都可以。 - kennytm

14

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。但我不会这么做,因为它可能会使代码难以阅读。请保持简洁明了。
  • Conal 的 TypeCompose 提供了一个叫做 result(.) 同义词。或许这个名称更有助于理解发生了什么。
  • 如果引入相关实例(import Control.Applicative 就可以),则 fmap 也可以代替 (.),但它的类型更通用,因此可能更令人困惑。
  • Conal 的 "fusion" 概念(不要与其他 "fusion" 用法混淆)与此有点相关,并且提供了一种很好的组合函数方法。更多详细信息可以在 Conal 进行的这个长达 Google Tech Talk 中获得

谢谢你的回答!我在Haskell方面还是很新手,所以有些看起来有点神秘,但了解不同的方法也是有帮助的 :) - guhou
2
“(。)(。)(。)” 的情况很常见和简单,我有时候为此创建一个“(...)” 运算符。但除此以外,现在可能是有意义的时候了。 - C. A. McCann

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接