Каррирование и замыкания
Поговорим об основных приёмах, доступных в функциональном программировании: каррировании и замыканиях.
Каррирование
Для функции двух аргументов оператор каррирования
выполняет преобразование
— берёт аргумент типа
и возвращает функцию типа
. С интуитивной точки зрения, каррирование функции позволяет фиксировать её некоторый аргумент, возвращая функцию от остальных аргументов. Таким образом,
— функция типа
.
Обратное преобразование называется декаррированием.
В хаскеле все функции изначально являются каррированными, т.е. они принимают 1 аргумент, продуцируя новую функцию, которая принимает ещё один аргумент и т.д. Есть 2 встроенные функции, позволяющие производить операции каррирования: curry и uncurry.
import Data.Typeable
curriedPower :: Integer -> Integer -> Integer
curriedPower a 1 = a
curriedPower a n = a * curriedPower a (n - 1)
main = do
print $ typeOf curriedPower
print $ curriedPower 2 10
let uncurriedPower = uncurry curriedPower
print $ typeOf uncurriedPower
print $ uncurriedPower (2, 10)
{-
OUTPUT:
Integer -> Integer -> Integer
1024
(Integer,Integer) -> Integer
1024
-}
Функция декаррирования создаёт функцию от 2-х аргументов, что в описании функции описано как (Integer,Integer) -> Integer. Каррированная форма является более удобной, поскольку она позволяет выполнять частичное применение функции. На практике это означает, что имеется возможность зафиксирововать 1 и более аргументов какими-либо значения, получив новую функцию с меньшим числом аргументов:
power :: Integer -> Integer -> Integer power a 1 = a power a n = a * power a (n - 1) sqr a = power a 2 base n = power 2 n main :: IO () main = do print $ sqr 4 print $ base 3
Поскольку фиксировать можно любые аргументы, а в хаскеле функции являются объектами первого класса и тоже могут быть переданы через аргументы, то можно писать и более сложные варианты для каррирующих функций:
import Data.List
insertionSort xs = foldr insert [] xs
main = do
let xs = insertionSort [5, 3, 9, 5, 6, 1, 2, 4, 8, 7]
print xs
Каррирование и частичное применение функции
Замыкания
В терминах хаскеля замыканием называется функция, которая использует свободные переменные, значение которых определяется окружением этой функции:
f x = (\y -> x + y)
main = do
print $ f 3 2
Функция (\y -> x + y) переменная x не используется, а её значение определяется извне через аргумент функции f. Стоит напомнить, что абстрактная функция имеет следующий вид записи: (\аргументы -> выражение), здесь обратная черта заменяет греческую букву λ, используемую в лямбда исчислении, а -> отделяет аргументы функции от тела функции. В математической форме описываемая функция записывается как .