Оператор точки в Haskell: требуется дополнительное объяснение


86

Я пытаюсь понять, что делает оператор точка в этом коде 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

Ответы:


138

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

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

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

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

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

Если вы все еще в замешательстве, это может помочь связать .что-то вроде конвейера UNIX. Если fвывод становится gвводом, а вывод становится hвводом, вы должны написать это в командной строке, например f < x | g | h. В Haskell .работает как 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


1
Маленькая придирка: первый фрагмент кода на самом деле не является действительным Haskell.
SwiftsNamesake

2
@SwiftsNamesake Для тех из нас, кто плохо владеет Haskell, вы имеете в виду, что единственный знак равенства здесь не имеет смысла? (Значит, фрагмент должен был быть отформатирован " f (g x)= (f . g) x"?) Или что-то еще?
user234461 02

1
@ user234461 Совершенно верно. ==Вместо этого вам понадобится действующий стандартный Haskell.
SwiftsNamesake 02

Этот маленький фрагмент вверху просто золотой. Как и другие ответы здесь правильные, но этот фрагмент просто интуитивно щелкнул в моей голове, что сделало ненужным читать остальную часть вашего ответа.
Тарик Веллинг,

24

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

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 - это одна функция.


23

sum- это функция в Haskell Prelude, а не аргумент sumEuler. Имеет тип

Num a => [a] -> a

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

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

Итак, у нас есть

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

Обратите внимание, что Intэто действительно экземпляр класса Numтипов.


11

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

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

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

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


5

Оператор точки в 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))

4

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.