Если блюда с карри можно найти обычно в хороших индийских ресторанах, функции карринга чаще встречаются в языках функционального программирования, к которым относятся ML и Haskell (см. раздел Ресурсы). Понятие карринг берет свои истоки от Хаскелла Карри (Haskell Curry), математика, разработавшего понятие частичных функций.
Понятие карринга означает передачу нескольких аргументов в функцию, которая принимает множество аргументов, и возвращает функцию, которая принимает оставшиеся аргументы и возвращает результат. Отличная новость - в текущей версии Groovy (на момент написания статьи это была версия jsr-02) поддерживается методcurry()
для объектов Closure
, а это значит, что мы, жители Планеты Groovy, теперь можем воспользоваться преимуществами некоторых аспектов функционального программирования! Возможно, до этого вы никогда не использовали карринг(curry()
), поэтому мы начнем с самых основ. В листинге 1 показано замыкание, обозначенное как multiply
. У него есть формальные параметры x
и y
, а возвращает оно произведение этих двух значений. После этого код предоставляет два метода для вызова замыкания multiply
: прямой (посредством call
) и косвенный, при условии отсутствия неопределенности. Последний стиль приводит нас к понятию функционального вызова.
def multiply = { x, y -> return x * y } // замыкание |
Это замыкание всем хорошо, но мы собираемся приправить его каррингом.
При вызове методаcurry()
не обязательно указывать весь список фактических параметров. Вызов с каррингом порождает создание частичного применения замыкания. Частичное применение замыкания - это еще один объект Closure
, в котором зафиксированы некоторые значения. В листинге 2 показано применение карринга к замыканию multiply
. В первом примере было установлено значение параметра x
, равное 3. Фактически у замыкания, обозначенного triple
, теперь имеется определение вида triple = { y -> return 3 * y }
.
def multiply = { x, y -> return x * y } // замыкание |
Как вы можете видеть, из определения 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