Каррирование в JS | Кодементор

Привет, мир!

JavaScript — это мультипарадигменный язык программирования, что означает, что он поддерживает как процедурное, так и функциональное программирование. Функциональное программирование позволяет делать забавные, почти волшебные вещи. Показательный пример: карри. В этом посте мы попытаемся понять, что такое каррирование и как его реализовать простым способом.

Функции высшего порядка

Чтобы реализовать каррирование, нам нужно узнать о функциях более высокого порядка. Так что же такое ХОФ?

Когда мы говорим о функциях высшего порядка, мы имеем в виду функцию, которая либо принимает одну или несколько функций в качестве аргументов, либо возвращает функцию в качестве результата.

Вы найдете только функции более высокого порядка в языках программирования, которые поддерживают первоклассные функции (или рассматривают функции как первоклассные граждане). Это означает язык, который позволяет нам хранить функции в переменных, передавать их как параметры в вызовах функций или возвращать их в результате вызова другой функции.

Вау! Звучит сложно. Ну, это не так. Потому что, скорее всего, вы уже используете HOF в своем коде. Помните карту, фильтр и уменьшение? Это функции высшего порядка.

Давайте создадим простую функцию высшего порядка.

const doubleFunc = fn => {
  return (...args) => {
    return 2 * fn(...args);
  };
};

Так что здесь происходит?

Мы написали функцию под названием doubleFunc который принимает функцию fn в качестве аргумента и возвращает новую функцию, которая принимает произвольное количество аргументов. После того, как мы предоставим это произвольное количество аргументов этой функции, она затем применяется fn к ним, умножает результат на 2 и возвращает результат.

Мы собрали произвольное количество аргументов в массив args выше, используя оператор расширения/остатка (…) в ES6. Подробнее об этом читайте в потрясающей книге Кайла Симпсона. здесь.

const add = (x, y) => x + y;
const doubleAdd = doubleFunc(add);

console.log(doubleAdd(1, 4)); //=> 10

Прохладный? Прохладный.

карри

Теперь, когда мы понимаем, что такое функция высшего порядка, как мы можем использовать это знание для создания каррирующей утилиты? Давайте сначала определим каррирование.

Каррирование — это процесс получения функции с несколькими аргументами и возврата ряда функций, которые принимают один аргумент и в конечном итоге разрешаются в значение.

Ладно, это немного сложно уложить в голове. Проще говоря, каррирование — это процесс преобразования функции, которая принимает несколько аргументов, в функцию, которая принимает меньше аргументов, путем исправления некоторых аргументов.

Давайте напишем пару инкрементных функций, изменив add.

const add = x => y => x + y;
const inc1 = add(1);
const inc2 = add(2);

inc1(4); //=> 5
inc2(4); //=> 6

Большой! Каррируя наши add мы сделали ее логику многоразовой и вытащили из нее 2 функции.

Это хорошо и все такое, но как мы можем каррировать любую функцию? Напишем для этого утилиту.

function curry(fn) {
  return function(...args) {
    if (args.length >= fn.length) {
      return fn(...args);
    }

return function(...more) {
      return curry(fn)(...args, ...more);
    };
  };
}

Ладно, давай разберёмся. Мы написали функцию под названием curry который принимает функцию fn в качестве аргумента и возвращает функцию n-арности (функцию, которая принимает произвольное количество аргументов). Эта функция делает несколько вещей.

  1. Если количество аргументов, предоставленных этой функции, равно или больше, чем количество аргументов, необходимых функции fnон просто вызывает fn с этими аргументами.
  2. В противном случае он возвращает другую функцию n-арности, которая при предоставлении некоторого more аргументы, пытается вызвать каррированный fn с args а также more.

Этот процесс продолжается до тех пор, пока не будет выполнено условие шага 1.

Если вы не понимаете, что здесь происходит, я рекомендую вам скопировать эту утилиту, добавить к ней регистраторы и использовать ее для создания собственной простой функции.

Приведенный выше фрагмент слишком многословен. Давайте перепишем его с помощью стрелочных функций.

const curry = fn => (...args) =>
  args.length >= fn.length ? 
  fn(...args) :
  (...more) => curry(fn)(...args, ...more);

Гораздо круче! Давайте воспользуемся этой вновь обретенной силой.

const sort = (ascend, list) => {
  const ascCmp = (a, b) => a > b;
  const dscCmp = (a, b) => a < b;
  return [...list].sort(ascend ? ascCmp : dscCmp);
};

const curriedSort = curry(sort);

const sortAsc = curriedSort(true);

const sortDsc = curriedSort(false);

const data = [9, 99, 29, 42, 45, 57, 13];

sortAsc(data); //=> [9, 13, 29, 42, 45, 57, 99]

sortDsc(data); //=> [99, 57, 45, 42, 29, 13, 9]

карри sortмы могли частично применять функцию, зафиксировав значение ascend флаг и сохранение полученной функции, которая теперь принимает только список чисел, в отдельную переменную.

Частичное применение функций может помочь нам повторно использовать наши функции более чистым способом. Мы можем даже передавать эти частично примененные функции в наше приложение для отложенной оценки по строке выполнения, когда нам будут доступны остальные аргументы.

Функции высшего порядка — это хлеб с маслом функционального программирования, а каррирование и частично применяемые функции позволяют использовать функции высшего порядка гораздо более эффективно и лаконично. Но HOF позволяют гораздо больше, чем просто каррирование, такие вещи, как композиция, запоминание и преобразование и т. д.

Если вам понравился этот вкус функционального программирования в JS, вы должны проверить эти библиотеки функционального программирования: В рамке а также Лодаш/fp.

Это все для карри, ребята. Надеюсь, вы узнали что-то об этой классной технике. Если вам понравился этот пост, пожалуйста, похлопайте и поделитесь. Чао!

Похожие записи

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *