Dot Operator в Haskell: нужно больше объяснений

Я пытаюсь понять, что делает оператор точки в этом коде на Haskell:

sumEuler = sum . (map euler) . mkList

Весь исходный код ниже.

Мое понимание

Точечный оператор принимает две функции sum и результат map euler и результат mkList в качестве входа.

Но, sum не функция ли это аргумент функции, верно? и так, что здесь происходит?

Кроме того, что является (map euler) делать?

Код

mkList :: Int -> [Int]
mkList n = [1..n-1]

euler :: Int -> Int
euler n = length (filter (relprime n) (mkList n))

sumEuler :: Int -> Int
sumEuler = sum . (map euler) . mkList

6 ответов

Решение

Проще говоря, . это композиция функций, как в математике:

f (g x) = (f . g) x

В вашем случае вы создаете новую функцию, sumEuler это также можно определить так:

sumEuler x = sum (map euler (mkList x))

Стиль в вашем примере называется стилем "без точек" - аргументы функции опущены. Это делает для более ясного кода во многих случаях. (Может быть трудно впасть в первый раз, когда вы видите это, но вы привыкнете к нему через некоторое время. Это обычная идиома Хаскелла.)

Если вы все еще в замешательстве, это может помочь связать . к чему-то вроде трубы UNIX. Если fвыходной становится gвход, чей вывод становится hвход, вы бы написать это в командной строке, как f < x | g | h, В Хаскеле . работает как UNIX |, но "задом наперед" - h . g . f $ x, Я считаю эту запись весьма полезной, например, при обработке списка. Вместо какой-то громоздкой конструкции вроде map (\x -> x * 2 + 10) [1..10]Вы могли бы просто написать (+10) . (*2) <$> [1..10], (И, если вы хотите применить эту функцию только к одному значению; это (+10) . (*2) $ 10, Последовательная!)

На вики Haskell есть хорошая статья с более подробной информацией: http://www.haskell.org/haskellwiki/Pointfree

. Оператор составляет функции. Например,

a . b

Если a и b являются функциями, это новая функция, которая запускает b в своих аргументах, тогда a в этих результатах. Ваш код

sumEuler = sum . (map euler) . mkList

точно так же, как:

sumEuler myArgument = sum (map euler (mkList myArgument))

но, надеюсь, легче читать. Причина, по которой вокруг карты euler есть параны, состоит в том, что она проясняет, что составляются 3 функции: sum, map euler и mkList - map euler - это одна функция.

sum это функция в прелюдии Haskell, а не аргумент sumEuler, Имеет тип

Num a => [a] -> a

Оператор композиции функций . имеет тип

(b -> c) -> (a -> b) -> a -> c

Итак, мы имеем

sum                        :: Num a => [a] -> a
map                        :: (a -> b) -> [a] -> [b]
euler                      :: Int -> Int
mkList                     :: Int -> [Int]
(map euler)                :: [Int] -> [Int]
(map euler) . mkList       :: Int -> [Int]
sum . (map euler) . mkList :: Int -> Int

Обратите внимание, что Int это пример Num,

. оператор используется для композиции функций. Точно так же как математика, если вам нужно функции f(x) и g(x) f . g становится f(g(x)).

Карта - это встроенная функция, которая применяет функцию к списку. Помещая функцию в скобки, функция рассматривается как аргумент. Термин для этого карри. Вы должны посмотреть это.

Что делает, так это то, что она принимает функцию с двумя аргументами, применяет аргумент эйлера. (карта эйлера) верно? и результатом является новая функция, которая принимает только один аргумент.

сумма (карта эйлера) . mkList - по сути, причудливый способ собрать все это вместе. Должен сказать, мой Haskell немного заржавел, но, может быть, вы сами соберете эту последнюю функцию?

Точка Оператор в Хаскеле

Я пытаюсь понять, что делает оператор точки в этом коде на Haskell:

sumEuler = sum . (map euler) . mkList

Короткий ответ

Эквивалентный код без точек, просто

sumEuler = \x -> sum ((map euler) (mkList x))

или без лямбды

sumEuler x = sum ((map euler) (mkList x))

потому что точка (.) указывает на состав функции.

Более длинный ответ

Во-первых, давайте упростим частичное применение euler в map:

map_euler = map euler
sumEuler = sum . map_euler . mkList

Теперь у нас есть только точки. На что указывают эти точки?

Из источника:

(.)    :: (b -> c) -> (a -> b) -> a -> c
(.) f g = \x -> f (g x)

таким образом (.) это оператор сочинения.

компоновать

В математике мы могли бы написать композицию функций f(x) и g(x), то есть f(g(x)), как

(ж ∘ г) (х)

который может быть прочитан "F с G".

Таким образом, в Haskell, f ∘ g или f, составленные из g, могут быть записаны:

f . g

Композиция является ассоциативной, что означает, что f(g(h(x))), записанный с помощью оператора композиции, может оставить скобки без какой-либо двусмысленности.

То есть, поскольку (f ∘ g) ∘ h эквивалентно f ∘ (g ∘ h), мы можем просто написать f ∘ g ∘ h.

Кружить обратно

Возвращаясь к нашему более раннему упрощению, это:

sumEuler = sum . map_euler . mkList

просто означает, что sumEuler это неприменимая композиция этих функций:

sumEuler = \x -> sum (map_euler (mkList x))

Точечный оператор применяет функцию слева (sum) к выводу функции справа. В вашем случае вы объединяете несколько функций - вы передаете результат mkList в (map euler)и затем передать результат этого sum, Этот сайт имеет хорошее введение в некоторые концепции.

Другие вопросы по тегам