Рубрики
Без рубрики

Используйте функциональную композицию в JavaScript

Упростите ваши сложные преобразования с помощью функциональной композиции в JavaScript

Автор оригинала: Rémi.

Предварительное условие: я использую Carrying в этом посте, поэтому, если вы не знаете об этом, я призываю вас прочитать мой предыдущий пост: Carrying в JavaScript

Что такое функциональная композиция?

Функциональная композиция – это механизм объединения нескольких простых функций для создания более сложной. Результат каждой функции передается следующему. В математике мы часто пишем что-то вроде: f (g (x)) Отказ Так что это результат g (x) это передано на F Отказ В программировании мы можем добиться состава, написав что-то подобное. Давайте возьмем быстрый пример. Предположим, мне нужно сделать некоторую арифметику, выполняя следующую операцию: 2 + 3 * 5 Отказ Как вы можете знать, умножение имеет приоритет над добавлением. Так вы начинаете с расчета 3 * 5 а потом, когда добавить 2 к результату. Давайте напишем это в JavaScript. Первичный и, безусловно, самый простой подход может быть:

const add = (a, b) => a + b;
const mult = (a, b) => a * b;
add(2, mult(3, 5))

Это форма композиции функций, поскольку это результат умножения, которая передается на Добавить функция. Давайте пойдем еще дальше и посмотрим другой случай, когда композиция функции может быть очень полезной. Предположим, теперь у меня есть список пользователей, и мне нужно извлечь имя всех взрослых пользователей. Я бы персонал написал что-то вроде:

const users = [
  { name: "Jeff", age: 14 },
    { name: "Jack", age: 18 }, 
    { name: "Milady", age: 22 },
]
const filter = (cb, arr) => arr.filter(cb);
const map = (cb, arr) => arr.map(cb);

map(u => u.name, filter(u => u.age >= 18, users)); //["Jack", "Milady"]

Это хорошо, но это может быть лучше, если бы мы смогли автоматизировать композицию. По крайней мере, это может быть более читаемо.

Автоматизировать композицию функции

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

compose(function1, function2, ... , functionN): Function

Так, например, мы хотели бы назвать такую функцию:

compose(add1, add2)(3) //6

Таким образом, реализация такой функции может быть:

const compose = (...functions) => args => functions.reduceRight((arg, fn) => fn(arg), args);

Разве это не круто? Эта только одна функция строки позволяет сочинять любую функцию для создания сложной трансформации. Позвольте мне объяснить, что здесь происходит:

  • составить является функцией высокого порядка. Это функция, которая возвращает другую функцию.
  • составить принимает несколько функций в качестве аргументов, и мы преобразуем их в массив функций с использованием распределяемого Opeartor: ...
  • Тогда мы просто применяем Редустера на этих функциях. Первый параметр обратного вызова – текущий аргумент. Второй аргумент является текущей функцией. Затем мы называем каждую функцию с текущим аргументом, и результат используется для следующего вызова.

Теперь мы можем использовать эту функцию на нашем предыдущем примере. Я VoloTarly Slevify карта и функции фильтра, чтобы она более читаема:

const filter = cb => arr => arr.filter(cb);
const map = cb => arr => arr.map(cb);

compose(
  map(u => u.name),
  filter(u => u.age >= 18)
)(users) //["Jack", "Milady"]

Я предлагаю вам последний пример. Давайте реализовать традиционный мир.

MapReduce с функциональной композицией

Принцип Maprecuce прост. Он просто применяет карту на набор данных и уменьшить результат для получения одного результата. Это, как правило, принцип функциональной композиции. Так, например, мы можем реализовать счетчик традиционного слова, чтобы подсчитать ряд слов, например. Карта будет нести ответственность за просто отправку 1, когда она сталкивается со значением, и уменьшение будет подведена до окончательного массива для получения результата:

const reduce = cb => arr => arr.reduce(cb); //Just currify the reduce function

const mapWords = map(() => 1);
const reduceWords = reduce((acc, curr) => acc += curr)(0)

compose(reduceWords, mapWords)(['foo', 'bar', 'baz']); //3

Труба или композиция?

Я добавил эту часть с Yeiber Cano упомянул, что я впервые реализовал труба вместо составить Отказ Вы можете прочитать его комментарий чуть ниже этой статьи.

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

const pipe = (...functions) => args => functions.reduce((arg, fn) => fn(arg), args);

Так что в этом случае мы используем Уменьшить вместо Редустера выполнить композицию слева направо.

Затем мы можем применить нашу недавно созданную функцию на наши предыдущие примеры:

pipe(
  filter(u => u.age >= 18),
  map(u => u.name),
)(users) //["Jack", "Milady"]

pipe(mapWords, reduceWords)(['foo', 'bar', 'baz']);

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

Заключение

Это тривиальный пример здесь, но осознавать, что вы можете использовать его на более сложных. Составная функция реализована на самых функциональных библиотеках, таких как Лодаш или Рамда. Вы даже можете найти вариант на функциональной композиции. Например, Ramda предложит composep Функция, которая позволяет создавать функции, которые возвращают обещания: http://ramdajs.com/docs/#Comsocep.