Каррирование в JavaScript и PHP
Содержание
Что такое каррирование
Каррирование - это процесс превращения функции, принимающей несколько аргументов, в функцию, которая принимает только один аргумент и возвращает новую функцию, которая ожидает следующий аргумент и так далее.
Каррирование может быть полезным, когда вы хотите создавать функции с частично заданными аргументами. Например, если у вас есть функция, которая складывает два числа, то вы можете использовать каррирование, чтобы создать новую функцию, которая всегда будет складывать определенное число с переданным ей числом.
Каррирование и замыкания
Каррирование и замыкания связаны друг с другом. Каррирование использует замыкания, чтобы сохранить значения аргументов функции между вызовами.
При каррировании функция принимает один аргумент и возвращает другую функцию, которая принимает следующий аргумент, и так далее, пока не будут переданы все аргументы. Каждая возвращаемая функция имеет доступ к аргументам, переданным на предыдущих этапах каррирования, благодаря замыканиям.
Примеры каррирования в JavaScript и PHP
Давайте начнем с JavaScript, так как в нем каррирование используется чаще.
Простой пример:
function curryAdd(x) { return function (y) { return x + y } } const addFive = curryAdd(5) console.log(addFive(3)) // Выводит 8
Данный код демонстрирует каррирование одноаргументной функции, где первый аргумент фиксирован, а второй передается при вызове каррированной функции.
- Функция
curryAdd
: Эта функция принимает один аргументx
и возвращает новую функцию, которая принимает другой аргументy
. Возвращаемая функция при вызове складываетx
иy
. - Применение
curryAdd
: Мы вызываемcurryAdd(5)
, получая новую функцию, в которойx
фиксировано и равно5
. - Вызов
addFive(3)
: Здесь мы вызываем функциюaddFive
, переданную изcurryAdd
, с аргументом3
. Посколькуx
уже равно5
, возвращаемое значение равно5 + 3
, то есть8
.
Сложный пример:
function curry(fn, ...args) { return (..._arg) => { return fn(...args, ..._arg) } } function multiply(a, b, c) { return a * b * c } const curriedMultiply = curry(multiply, 2) console.log(curriedMultiply(3, 4)) // Выводит 24
Этот код демонстрирует каррирование с частичным применением аргументов, где один аргумент передается заранее, а остальные передаются позже. Это позволяет создать новую функцию, которая может быть вызвана с меньшим количеством аргументов.
- Функция
curry
: Эта функция принимает другую функциюfn
и неопределённое количество аргументов...args
. Она возвращает новую функцию, которая может быть вызвана с дополнительными аргументами..._arg
. При вызове внутренней функции аргументыargs
и_arg
объединяются, и результат передаётся в исходную функциюfn
. - Функция
multiply
: Это обычная функция, принимающая три аргумента и возвращающая их произведение. - Применение
curry
кmultiply
: Мы вызываем функциюcurry
, передавая ей функциюmultiply
и фиксированный аргумент2
. Это возвращает новую функцию, которая ожидает два оставшихся аргумента.const curriedMultiply = curry(multiply, 2);
- Вызов
curriedMultiply(3, 4)
: Мы вызываем каррированную функцию с двумя аргументами3
и4
. Внутриcurry
, эти аргументы объединяются с уже заданным2
, и исходная функцияmultiply
вызывается с тремя аргументами:2
,3
, и4
, что возвращает24
.console.log(curriedMultiply(3, 4)); // Выводит 24
В PHP каррирование менее распространено, но его все равно можно реализовать
Простой пример:
function curryMultiply($a) { return function ($b) use ($a) { return function ($c) use ($a, $b) { return $a * $b * $c; }; }; } $curried = curryMultiply(2); $curriedWithB = $curried(3); $result = $curriedWithB(4); // Выводит 24
Здесь мы имеем функцию curryMultiply
, которая принимает три аргумента: a
, b
и c
. Благодаря каррированию, мы можем передать эти аргументы последовательно, получая новую функцию на каждом этапе. Это позволяет нам создавать частично примененные функции, такие как curried
, которые затем могут быть дополнены другими аргументами, как curriedWithB
, и так далее.
Сложный пример:
function curry($fn, ...$args) { return function (...$newArgs) use ($fn, $args) { return $fn(...array_merge($args, $newArgs)); }; } function multiply($a, $b, $c) { return $a * $b * $c; } $curriedMultiply = curry('multiply', 2); echo $curriedMultiply(3, 4); // Выводит 24
Важно понимать, что этот пример не является строгим каррированием в классическом понимании, поскольку каррированная функция принимает не один аргумент за раз, а два. Однако это демонстрирует близкую идею, где часть аргументов может быть фиксирована заранее, а остальные переданы позже.
- Функция
curry
: Эта функция принимает имя функции$fn
, которую нужно каррировать, и список аргументов$args
, которые будут фиксированными. Она возвращает новую функцию, которая принимает дополнительные аргументы$newArgs
. - Функция
multiply
: Это простая функция, которая принимает три аргумента и возвращает их произведение. - Применение
curry
кmultiply
: Мы вызываем функциюcurry
, передавая ей имя функцииmultiply
и фиксированный аргумент2
. Это возвращает новую функцию, которая принимает два аргумента (поскольку первый аргумент уже задан и равен2
).$curriedMultiply = curry('multiply', 2);
- Вызов
$curriedMultiply(3, 4)
: Здесь мы вызываем каррированную функцию, передавая ей два аргумента3
и4
. Внутри каррированной функции эти аргументы объединяются с уже заданным аргументом2
, и затем вызывается исходная функцияmultiply
с тремя аргументами2
,3
и4
.echo $curriedMultiply(3, 4); // Выводит 24
Эти примеры демонстрируют, как можно преобразовать функцию, принимающую несколько аргументов, в цепочку функций, принимающих по одному аргументу, и как это может быть полезно для частичного применения функций.
Если все равно не понятно?
Давайте рассмотрим еще один пример на языке JavaScript. Допустим, у нас есть функция sum
, которая складывает два числа:
function sum(a, b) { return a + b }
Если мы хотим сложить два числа 2 и 3, мы можем вызвать эту функцию следующим образом:
const result = sum(2, 3) // результат: 5
В этом примере мы передаем два аргумента - 2
и 3
- в функцию sum
, которая возвращает сумму этих чисел.
function sum(a) { return function (b) { return a + b } }
В этой новой версии функции sum
мы принимаем только один аргумент a
, а затем возвращаем функцию, которая принимает аргумент b
и возвращает сумму a
и b
.
Теперь, чтобы сложить два числа 2 и 3 с использованием каррированной функции sum
, мы вызываем ее следующим образом:
const result = sum(2)(3) // результат: 5
Здесь мы вызываем функцию sum
с аргументом 2
, что возвращает другую функцию, которую мы тут же вызываем с аргументом 3
. Результатом является сумма 2
и 3
, равная 5
.
Таким образом, каррирование позволяет нам преобразовывать функции, принимающие несколько аргументов, в функции, которые можно вызывать по одному аргументу, что делает код более читаемым и модульным.