Почему функция pointfree этой функции выглядит так?

Я играю с Haskell с честью, в том числе и с практическими функциями написания в бесконтактной форме. Вот примерная функция:

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

Я хотел бы написать эту функцию в свободной форме. Вот пример, который я нашел в другом месте:

dotProduct = (sum .) . zipWith (*)

Однако я не понимаю, почему точка без формы выглядит как (sum .) . zipWith (*) вместо sum . zipWith (*). Почему сумма заключена в скобки и имеет 2 композиционных оператора?

Ответы

Ответ 1

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

Ответ 2

Ответ 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. Возможно, это имя более полезно для понимания того, что происходит.
    • fmap также работает вместо (.), если импортировать соответствующие экземпляры (import Control.Applicative сделает это), но его тип более общий и, следовательно, может быть более запутанным.
  • Концепция конуса "слияние" (не путать с другими обычаями "слияния" ) является родственной, и imho предлагает хороший способ создания функций. Подробнее в этот длинный Google Tech Talk, который дал Конал