На информационном ресурсе применяются рекомендательные технологии (информационные технологии предоставления информации на основе сбора, систематизации и анализа сведений, относящихся к предпочтениям пользователей сети "Интернет", находящихся на территории Российской Федерации)

Практически Groovy : Функциональное программирование с помощью использованием замыканий и карринга. Часть 2

Передайте карри, пожалуйста!

Если блюда с карри можно найти обычно в хороших индийских ресторанах, функции карринга чаще встречаются в языках функционального программирования, к которым относятся ML и Haskell (см. раздел Ресурсы). Понятие карринг берет свои истоки от Хаскелла Карри (Haskell Curry), математика, разработавшего понятие частичных функций.

Понятие карринга означает передачу нескольких аргументов в функцию, которая принимает множество аргументов, и возвращает функцию, которая принимает оставшиеся аргументы и возвращает результат. Отличная новость - в текущей версии Groovy (на момент написания статьи это была версия jsr-02) поддерживается метод curry() для объектов Closure, а это значит, что мы, жители Планеты Groovy, теперь можем воспользоваться преимуществами некоторых аспектов функционального программирования!

Возможно, до этого вы никогда не использовали карринг(curry()), поэтому мы начнем с самых основ. В листинге 1 показано замыкание, обозначенное как multiply. У него есть формальные параметры x и y , а возвращает оно произведение этих двух значений. После этого код предоставляет два метода для вызова замыкания multiply: прямой (посредством call) и косвенный, при условии отсутствия неопределенности. Последний стиль приводит нас к понятию функционального вызова.


Листинг 1. Просто замыкание

def multiply = { x, y -> return x * y } // замыкание
def p = multiply.call(3, 4) // прямоый вызов
def q = multiply(4, 5) // косвенный вызов
println "p: ${p}" // p = 12
println "q: ${q}" // q = 20

 

Это замыкание всем хорошо, но мы собираемся приправить его каррингом.

При вызове метода curry() не обязательно указывать весь список фактических параметров. Вызов с каррингом порождает создание частичного применения замыкания. Частичное применение замыкания - это еще один объект Closure, в котором зафиксированы некоторые значения.

В листинге 2 показано применение карринга к замыканию multiply. В первом примере было установлено значение параметра x, равное 3. Фактически у замыкания, обозначенного triple, теперь имеется определение вида triple = { y -> return 3 * y }.


Листинг 2. Замыкания с карри

def multiply = { x, y -> return x * y }  // замыкание
def triple = multiply.curry(3) // triple = { y -> вовзрат 3 * y }
def quadruple = multiply.curry(4)
// quadruple = { y -> return 4 * y }
def p = triple.call(4) // прямой вызов
def q = quadruple(5) // косвенный вызов
println "p: ${p}" // p = 12
println "q: ${q}" // q = 20

 

Как вы можете видеть, из определения multiply был удален параметр x, а все его упоминания были заменены значением 3.

Начала математики с карри

Как вы знаете из основ математики, оператор умножения является перестановочным (другими словами, x * y = y * x). С другой стороны, оператор вычитания перестановочным не является; поэтому для обработки уменьшаемого и вычитаемого нужны две операции. В листинге 3 для этой цели определены замыкания lSubtract и rSubtract (для левой и правой части соответственно), а после этого показано интересное применение функции curry.


Listing 3. Left and right operands

                
def lSubtract = { x, y -> return x - y }
def rSubtract = { y, x -> return x - y }
def dec = rSubtract.curry(1)
// dec = { x -> return x - 1 }
def cent = lSubtract.curry(100)
// cent = { y -> return 100 - y }
def p = dec.call(5) // прямой вызов
def q = cent(25) // косвенный вызов
println "p: ${p}" // p = 4
println "q: ${q}" // q = 75
наверх