Dot Operator в Haskell: нужно больше объяснений
-
08-07-2019 - |
Вопрос
Я пытаюсь понять, что делает оператор точки в этом коде на Haskell:
sumEuler = sum . (map euler) . mkList
Весь исходный код приведен ниже. Р>
Мое понимание
Оператор точки принимает в качестве входных данных две функции sum
и результат map euler
и результат mkList
. Р>
Но (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
Решение
Проще говоря, .
- это композиция функций, как в математике:
f (g x) = (f . g) x
В вашем случае вы создаете новую функцию, sumEuler
которую также можно определить следующим образом:
sumEuler x = sum (map euler (mkList x))
Стиль в вашем примере называется " point-free " стиль - аргументы функции опущены. Это делает для более ясного кода во многих случаях. (Может быть трудно впасть в первый раз, когда вы видите это, но вы привыкнете к нему через некоторое время. Это распространенная идиома Хаскелла.)
Если вы все еще в замешательстве, это может помочь связать f
с чем-то вроде конвейера UNIX. Если вывод g
становится вводом h
, вывод которого становится вводом f < x | g | h
, вы напишите это в командной строке, как |
. В Haskell h . g . f $ x
работает как UNIX map (\x -> x * 2 + 10) [1..10]
, но & Quot; в обратном направлении & Quot; - (+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))
но, надеюсь, легче читать. Причина, по которой вокруг map 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 немного заржавел, но, может быть, вы сами соберете эту последнюю функцию?
Оператор точки применяет функцию слева (sum
) к выводу функции справа. В вашем случае вы объединяете несколько функций в цепочку - вы передаете результат mkList
в (map euler)
, а затем передаете результат в <=>.
Этот сайт имеет хорошее введение в некоторые концепции.
Оператор точек в Хаскеле
Я пытаюсь понять, что делает оператор точки в этом коде на 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 & # 8728; g) (x)
который можно прочитать " f, составленный с помощью g " ;. Р>
Итак, в Хаскеле, f & # 8728; g или f, составленные с помощью g, можно записать так:
f . g
Композиция является ассоциативной, что означает, что f (g (h (x))), записанный с помощью оператора композиции, может исключить скобки без какой-либо двусмысленности.
То есть, поскольку (f & # 8728; g) & # 8728; h эквивалентно f & # 8728; (g & # 8728; h), мы можем просто написать f & # 8728; г & # 8728; ч. р>
Возвращаясь назад
Возвращаясь к нашему более раннему упрощению, это:
sumEuler = sum . map_euler . mkList
просто означает, что sumEuler
является неприменимой композицией этих функций:
sumEuler = \x -> sum (map_euler (mkList x))