На этом шаге мы рассмотрим функционалы curry и uncurry.
Представление функций многих аргументов в виде последовательности функций одного аргумента предложил М.Шейнфинкель (1924): если функция y=f(x1,x2,...,xn) имеет тип
A1 → (A2 → (... → (An → B)...)),
(...(f(a1) a2)...) an,
(...(f(x1) x2)...) xn.
В качестве синонима слова "карринг" используются термины частичное применение функции и частичная параметризация.
Каррированные функции "предполагают", что их можно применять к неполному числу аргументов, и результатом такого применения будут новые функции, которые "ожидают" на свой вход оставшиеся аргументы.
В частности, применяя карринг, можно избавиться от составного объекта - пары, получив функцию, зависящую от двух простых объектов (чисел), которые являлись компонентами пары.
Библиотека Prelude содержит два функционала
curry:: ((a,b) -> c) -> (a -> b -> c) curry f x y = f (x,y) uncurry:: (a -> b -> c) -> ((a,b) -> c) uncurry f (x,y) = f x y
Отметим преимущества частично применённых функций:
В частности, использование механизма карринга предпочтительнее работы с парами. Каррированную функцию можно частично вычислить (параметризовать), а функцию, использующую упорядоченную пару аргументов - нет (поэтому, в частности, все стандартные функции, зависящие более чем от одного параметра, записаны в карринговой форме).
Поясним последнее утверждение.
twice:: (Double -> Double) -> (Double -> Double) twice f x = f (f x)
Используя исчисление комбинаторов, легко получить комбинаторное представление функции twice:
f(fx)=Bffx=((.) f f) x,
twice':: (Double -> Double) -> (Double -> Double) twice' f = (.) f f
Получено представление функции в карринговой форме, которое характеризуется отсутствием аргументов:
twice':: (Double -> Double) -> (Double -> Double) twice' f = (.) f f
Определение twice в некарринговой форме выглядит так:
twice'':: (Double -> Double) -> (Double -> Double) twice'' (f,x) = f (f x)
Теперь уже невозможно вызвать функцию без указания аргумента, к которому функция применяется.
Для тестирования можно использовать функцию
test = (twice sin 1,(twice' sin 1,twice'' (sin,1)))
Итак, в случае необходимости всегда можно преобразовать функцию к виду частично применённой с помощью функции curry, которая берёт "некарринговую" функцию и преобразует её к частично применённой форме.
Отметим, что функция curry сама представляет собой частично применённую функцию (она получает три аргумента, один за другим).
Функция uncurry осуществляет обратное преобразование: она превращает функцию с двумя параметрами в её некарринговую форму.
В заключение приведем несколько демонстрационных примеров.
-- Демонстрация некаррированной функции "умножение" -- (вызов функции аналогичен принятому в математике) ---------------------------------------------------- mUnCur:: (Int,Int) -> Int mUnCur (a,b) = a*b -- ********************************************** -- Демонстрация каррированной функции "умножение" -- (вызов функции аналогичен принятому в функцио- -- нальном программировании) ----------------------------- mulCur:: Int -> Int -> Int mulCur a b = a*b -- ************************************************** -- Демонстрация удобного изменения порядка аргументов -- в каррированной функции -------------------------------------- flip':: (t -> u -> v) -> (u -> t -> v) flip' f b a = f a b -- *********************************** -- Демонстрация преобразования функции -- в каррированную форму -------------------------------------- curry':: ((t,u) -> v) -> (t -> u -> v) curry' g a b = g (a,b) -- *********************************** -- Демонстрация преобразования функции -- в некаррированную форму ---------------------------------------- uncurry':: (t -> u -> v) -> ((t,u) -> v) uncurry' f (a,b) = f a b -- **************************************** -- Моделирование функции для декаррирования ------------------------------------------- uncurry'' f p = f (fst p) (snd p) -- ****************************** -- Неудачные тестовые примеры: ------------------------------------ test1 = mUnCur (3,4) == 12 && curry' mUnCur 4 5 == 20 && uncurry' mulCur (4,5) == 20 test2 = uncurry'' (-) (2,5) == uncurry (-) (2,5)
На следующем шаге мы приведем некоторые утилиты для работы со списками.