...

четверг, 29 января 2015 г.

Немного о каррировании в Haskell

Читая М. Липовача «Изучай Haskell во имя добра!», я поначалу не понимал, чем частичное применение отличается от каррирования. Потратил некоторое время на разбор данного вопроса и набросал себе «шпаргалку» по обозначенной теме.



В Haskell функции без параметров называются константными функциями, поскольку каждая из них всегда возвращает одно и то же значение.

func :: String
func = "Haskell"




Функции не могут принимать более одного параметра. Если функция, принимает несколько параметров, то на самом деле она является функцией с одним параметром и возвращает другую функцию, которая так же принимает лишь один параметр и возвращает некоторый результат (др. функцию или конкретное значение).Т.е. если для функции указана такая сигнатура:

func :: Int -> Double -> Char -> Bool



то Haskell воспринимает её следующим образом:

func :: Int -> (Double -> (Char -> Bool))



Т.е. функция func принимает параметр типа Int и возвращает новую функцию, которая принимает очередной параметр — типа Double и возвращает другую новую функцию, принимающую параметр типа Char и возвращающую значение типа Bool.

Преобразование функции от многих аргументов в функцию, берущую свои аргументы по одному называется каррированием. Haskell автоматически выполняет каррирование всех функций, принимающих более одного параметра. Именно благодаря каррированию становится возможным частичное применение функций, а так же создание сечений. В свою очередь, частичное применение делает возможным существование бесточечной нотации.

Примечание





В Haskell не существует такого понятия, как частичное применение функции. Существует применение функции (без «частично»). Если мы говорим (для удобства восприятия), что функция f :: Int -> Int -> Int имеет два аргумента, (что с технической точки зрения не является корректным), то мы можем так же сказать (снова для удобства восприятия), что f 5 — это частично применённая функция (что так же не будет корректно технически).





Пример



func :: (Num a) => a -> a -> a -> a
func a b c d = a + b + c + d




ghci




Частичное применение:

λ: let n = func 2 3
λ: let m = n 10
λ: let g = m 7
λ: g
22




Сечения:

λ: let a = (/2)
λ: a 10
5.0
λ: let b = (15/)
λ: b 5
3.0




Бесточечная нотация:

odd' :: Integral a => a -> Bool
odd' = odd




ghci:

λ: odd' 5
True
λ: odd' 4
False


Каррирование и декаррирование




В стандартном модуле Data.Tuple определены, помимо прочего, следующие функции:

curry :: ((a, b) -> c) -> a -> b -> c
uncurry :: (a -> b -> c) -> (a, b) -> c




Функция curry преобразовывает некаррированную функцию в каррированную.

Функция uncurry преобразовывает каррированную функцию в некаррированную.

Пример



msg :: Int -> Bool -> String
msg n True = show $ n `div` 2
msg n _ = show $ n * 2




ghci




λ: let n = msg
λ: let m = uncurry n
λ: :t n
n :: Int -> Bool -> String
λ: :t m
m :: (Int, Bool) -> String
λ: n 5 True
"2"
λ: m (5,True)
"2"
λ: let k = curry m
λ: :t k
k :: Int -> Bool -> String
λ: k 5 True
"2"


Recommended article: Chomsky: We Are All – Fill in the Blank.

This entry passed through the Full-Text RSS service - if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


Комментариев нет:

Отправить комментарий