Точковий оператор у Хаскелі: потрібні додаткові пояснення


86

Я намагаюся зрозуміти, що робить оператор крапок у цьому коді Хаскелла:

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))

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

Якщо ви все ще розгублені, це може допомогти зв'язатись .з чимось на зразок 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

Цей маленький фрагмент зверху - це просто золото. Як і інші відповіді тут, є правильними, але цей фрагмент просто клацнув безпосередньо інтуїтивно в моїй голові, що зробило непотрібним читати решту відповіді.
Тарік Веллінг,

24

. оператор складає функції. Наприклад,

a . b

Де a і b є функціями - це нова функція, яка запускає b на своїх аргументах, тоді a на цих результатах. Ваш код

sumEuler = sum . (map euler) . mkList

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

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

але, сподіваюся, легше читати. Причина існування парен навколо euler map полягає в тому, що стає зрозумілішим, що складаються 3 функції: sum , map euler і mkList - map euler є єдиною функцією.


23

sumє функцією в прелюдії Haskell, а не аргументом 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це справді екземпляр Numtypeclass.


11

. оператор використовується для складання функції. Так само, як математика, якщо вам доводиться функціонувати f (x) та g (x) f. g стає f (g (x)).

map - це вбудована функція, яка застосовує функцію до списку. Поставивши функцію в дужки, функція розглядається як аргумент. Термін для цього - каррі . Ви повинні це шукати.

Що робить, це те, що вона приймає функцію з двома аргументами, вона застосовує аргумент euler. (карта euler) так? і результатом є нова функція, яка приймає лише один аргумент.

сума. (ейлер карти). mkList - це, по суті, вигадливий спосіб поєднати все це разом. Треба сказати, мій Haskell трохи іржавий, але, можливо, ви можете скласти цю останню функцію самостійно?


5

Точковий оператор у Хаскелі

Я намагаюся зрозуміти, що робить оператор крапок у цьому коді Хаскелла:

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) (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.