Автор оригинала: Casey Morris.
Какая сделка здесь?
Функциональное программирование было на подъеме и является темой, которая очень интересно для меня. Это позволяет мне писать церный, декларативный код, который легко проверить и разум. Что такое функциональное программирование? Я отложу этот ответ кому-то с большим знанием на эту тему, Эрик Эллиот :
Функциональное программирование (часто сокращенные ФП) – это процесс строительства программного обеспечения, составив Чистые функции , избегая Общее состояние, Союзные данные и побочные эффекты Отказ Функциональное программирование – декларативный а не Императив и состояние приложения течет через чистые функции. Контраст с объектно-ориентированным программированием, где состояние приложения обычно передается и погружается с методами в объектах.
ES6 приносит многие функции, которые позволяют нам легко писать чистые функции, отдохнуть/распространяться одним из самых мощных. Использование Пословные параметры мы можем “цикл без петель” с Рекурсия Отказ В этой статье мы собираемся переписать многие широко используемые методы/функции JavaScript, которые позволяют функциональным шаблонам.
Предисловие
Следующие функции предназначены для демонстрации и обучения. Многие функции ниже представляют собой хвостовую рекурсию и должны быть оптимизированы дальше. Фиксация хвостовой рекурсии не является предметом этой статьи. ES6 приносит оптимизацию Call-Call, но должен использоваться в сочетании с «Используйте строгий»
Отказ
Глава
Верните первый элемент в массиве. Полезно, когда вам нужно отделить первый элемент от остальных элементов массива. Для этого мы используем Разрушение задания Отказ
const head = ([x]) => x
Пример использования:
const array = [1,2,3,4,5] head(array) // 1
Хвост
Верните все, кроме первого элемента в массиве.
const tail = ([, ...xs]) => xs
Что по сути так же, как написание:
const tail = ([x, ...xs]) => xs
Так как нам не нужно использовать х
В возвращенном выходе мы можем бросить его, но держать запятую, чтобы получить остальные предметы в массиве.
Пример использования:
const array = [1,2,3,4,5] tail(array) // [2,3,4,5]
Деф
Возврат, если аргумент определен.
const def = x => typeof x !== 'undefined'
Пример использования:
const defined = 'this is defined' def(defined) // true def(doesntExist) // false
Undef.
Возврат, если прилагаемый аргумент не определен.
const undef = x => !def(x)
Пример использования:
const defined = 'this is defined' undef(defined) // false undef(doesntExist) // true
Скопировать
Возвращает копию массива без использования Array.slice ()
Отказ Использует Распространение Отказ
const copy = array => [...array]
Пример использования:
let array = [1,2,3,4,5] let copied = copy(array) copied.push(6) array // [1,2,3,4,5] copied // [1,2,3,4,5,6]
Длина
Вернуть длину массива. Это очень простая форма цикла через массив с рекурсией, даже если значения массива не имеют значения в этом случае (увеличивает начиная с 1 для каждого элемента в массиве). Мы включаем Лен параметр, чтобы избежать хвост рекурсии Отказ
const length = ([x, ...xs], len = 0) => def(x) ? length(xs, len + 1) : len
Если мы не заботимся о рекурсии хвоста, мы можем написать это как:
const length = ([x, ...xs]) => def(x) ? 1 + length(xs) : 0
Это будет добавлять кадр стека для каждого элемента в массиве, тогда как версия, которая позволяет избежать рекурсионного хвоста, заменяет один кадр стека. Если массив прошел в большом достаточно большом, он будет бросать «максимальный размер стека вызовов».
Пример использования:
const array = [1,2,3,4,5] length(array) // 5
Обеспечить регресс
Вернуть обратный массив.
const reverse = ([x, ...xs]) => def(x) ? [...reverse(xs), x] : []
Пример использования:
const array = [1,2,3,4,5] reverse(array) // [5,4,3,2,1]
Array.Reverse ()
в порядке, но это изменяет значение на месте, которое является побочным эффектом. Рассмотрим следующее:
const array = [1,2,3,4,5] const newArray = array.reverse() // [5,4,3,2,1] array // [5,4,3,2,1] // using the reverse method we just created const array2 = [1,2,3,4,5] const newArray2 = reverse(array2) // [5,4,3,2,1] array2 // [1,2,3,4,5]
Первый
Возвращает новый массив, который содержит первые n элементов данного массива.
const first = ([x, ...xs], n = 1) => def(x) && n ? [x, ...first(xs, n - 1)] : []
Пример использования:
const array = [1,2,3,4,5] first(array, 3) // [1,2,3]
Последний
Возвращает новый массив, который содержит последние n элементов данного массива.
const last = (xs, n = 1) => reverse(first(reverse(xs), n))
Пример использования:
const array = [1,2,3,4,5] last(array, 3) // [3,4,5]
Ломтик
Возвращает новый массив со значением, вставленным в указанный индекс.
const slice = ([x, ...xs], i, y, curr = 0) => def(x) ? curr === i ? [y, x, ...slice(xs, i, y, curr + 1)] : [x, ...slice(xs, i, y, curr + 1)] : []
Пример использования:
const slice = ([x, ...xs], i, y, curr = 0) => def(x) ? curr === i ? [y, x, ...slice(xs, i, y, curr + 1)] : [x, ...slice(xs, i, y, curr + 1)] : []
isarray.
Возвращает, если прилагаемое значение является массивом. Позволяет нам написать Array.isarray ()
более функционально.
const isArray = x => Array.isArray(x)
Пример использования:
const array = [1,2,3,4,5] isArray(array) // true
Сплющить
Сочетает в себе вложенные массивы в один массив.
const flatten = ([x, ...xs]) => def(x) ? isArray(x) ? [...flatten(x), ...flatten(xs)] : [x, ...flatten(xs)] : []
Пример использования:
const array1 = [1,2,3] const array2 = [4,[5,[6]]] flatten([array1, array2]) // [1,2,3,4,5,6]
Менять
Верните новый массив с 2 пунктами, поменяемыми на основе их индекса.
const swap = (a, i, j) => ( map(a, (x,y) => { if(y === i) return a[j] if(y === j) return a[i] return x }) )
Пример использования:
const array = [1,2,3,4,5] swap(array, 0, 4) // [5,2,3,4,1]
карта
От MDN: «… создает новый массив с результатами вызова предоставленной функции на каждом элементе в этом массиве».
const map = ([x, ...xs], fn) => { if (undef(x)) return [] return [fn(x), ...map(xs, fn)] }
Которые могут быть упрощены как:
const map = ([x, ...xs], fn) => def(x) ? [fn(x), ...map(xs, fn)] : []
Пример использования:
const double = x => x * 2 map([1,2,3], double) // [2,4,6]
Фильтр
От MDN: «… создает новый массив со всеми элементами, которые передают тест, реализованный предоставленной функцией».
const filter = ([x, ...xs], fn) => { if (undef(x)) return [] if (fn(x)) { return [x, ...filter(xs, fn)] } else { return [...filter(xs, fn)] } }
Которые могут быть упрощены как:
const filter = ([x, ...xs], fn) => def(x) ? fn(x) ? [x, ...filter(xs, fn)] : [...filter(xs, fn)] : []
Пример использования:
const even = x => x % 2 === 0 const odd = x = !even(x) const array = [1,2,3,4,5] filter(array, even) // [2,4] filter(array, odd) // [1,3,5]
Отклонять
Противоположность фильтра, возвращает массив, который не передает функцию фильтра.
const reject = ([x, ...xs], fn) => { if (undef(x)) return [] if (!fn(x)) { return [x, ...reject(xs, fn)] } else { return [...reject(xs, fn)] } }
Пример использования:
const even = x => x % 2 === 0 const array = [1,2,3,4,5] reject(array, even) // [1,3,5]
Разбиение
Разбивает массив в две массивы. Тот, чьи элементы передают функцию фильтра и один элементы, отказоустойчивые.
const partition = (xs, fn) => [filter(xs, fn), reject(xs, fn)]
Пример использования:
const even = x => x % 2 === 0 const array = [0,1,2,3,4,5] partition(array, even) // [[0,2,4], [1,3,5]]
Уменьшать
Из MDN: «… применяет функцию на фоне аккумулятора и каждый элемент в массиве (слева направо), чтобы уменьшить его до одного значения».
const reduce = ([x, ...xs], fn, memo, i) => { if (undef(x)) return memo return reduce(xs, fn, fn(memo, x, i), i + 1) }
Которые могут быть упрощены как:
const reduce = ([x, ...xs], fn, memo, i = 0) => def(x) ? reduce(xs, fn, fn(memo, x, i), i + 1) : memo
Пример использования:
const sum = (memo, x) => memo + x reduce([1,2,3], sum, 0) // 6 const flatten = (memo, x) => memo.concat(x) reduce([4,5,6], flatten, [1,2,3]) // [1,2,3,4,5,6]
Редукторы
Похоже, что уменьшить, но применяет функцию из правого налево.
const reduceRight = (xs, fn, memo) => reduce(reverse(xs), fn, memo)
Пример использования:
const flatten = (memo, x) => memo.concat(x) reduceRight([[0,1], [2,3], [4,5]], flatten, []) // [4, 5, 2, 3, 0, 1]
Частичный
Частично применять функцию путем заполнения любого количества его аргументов.
const partial = (fn, ...args) => (...newArgs) => fn(...args, ...newArgs)
Пример использования:
const add = (x,y) => x + y const add5to = partial(add, 5) add5to(10) // 15
Раздача
Конвертировать функцию, которая принимает массив на один, который принимает несколько аргументов. Это полезно при частично применении.
const spreadArg = (fn) => (...args) => fn(args)
Пример использования:
const add = ([x, ...xs]) => def(x) ? parseInt(x + add(xs)) : [] add([1,2,3,4,5]) // 15 const spreadAdd = spreadArg(add) spreadAdd(1,2,3,4,5) // 15
Если вы хотите определить только одну функцию, вы можете написать ее как:
const add = spreadArg(([x, ...xs]) => def(x) ? parseInt(x + add(...xs)) : []) add(1,2,3,4,5) // 15
В приведенном выше вам нужно помнить, чтобы распределить массив, который вы отправляете функцию рекурсивно, поскольку вы распространяете аргумент.
Reverseargs.
Реверсивный заказ на аргумент функции.
const reverseArgs = (fn) => (...args) => fn(...reverse(args))
Пример использования:
const divide = (x,y) => x / y divide(100,10) // 10 const reverseDivide = reverseArgs(divide) reverseDivide(100,10) // 0.1
Реверсивные аргументы могут быть полезны при частично применении аргументов. Иногда вы хотите частично применить аргументы в конце списка, а не те, которые в начале. Реверсирование аргументов позволяет нам это сделать.
const percentToDec = partial(reverseDivide, 100) percentToDec(25) // 0.25
Вычеркивать
Извлечь значение свойства из массива. Полезно в сочетании с карта функция.
const pluck = (key, object) => object[key]
Пример использования:
const product = {price: 15} pluck('price', product) // 15 const getPrices = partial(pluck, 'price') const products = [ {price: 10}, {price: 5}, {price: 1} ] map(products, getPrices) // [10,5,1]
Поток
Каждая функция потребляет возвращаемое значение функции, которая пришла ранее.
const flow = (...args) => init => reduce(args, (memo, fn) => fn(memo), init)
Пример использования:
const getPrice = partial(pluck, 'price') const discount = x => x * 0.9 const tax = x => x + (x * 0.075) const getFinalPrice = flow(getPrice, discount, tax) // looks like: tax(discount(getPrice(x))) // -> get price // -> apply discount // -> apply taxes to discounted price const products = [ {price: 10}, {price: 5}, {price: 1} ] map(products, getFinalPrice) // [9.675, 4.8375, 0.9675]
Составить
Так же, как поток, но аргументы применяются в обратном порядке. Составьте спички более естественным образом с использованием функций. Используя те же данные, как определено для поток Функция:
const compose = (...args) => flow(...reverse(args))
Пример использования:
const getFinalPrice = compose(tax, discount, getPrice) // looks like: tax(discount(getPrice(x))) map(products, getFinalPrice) // [9.675, 4.8375, 0.9675]
Мин
Верните наименьшее число в массиве. Возвращает Бесконечность Если Array поставляется пуст.
const min = ([x, ...xs], result = Infinity) => def(x) ? x < result ? min(xs, x) : result : result
Пример использования:
const array = [0,1,2,3,4,5] min(array) // 0
Максимум
Верните наибольшее количество в массиве. Возвращает -Infinity Если Array поставляется пуст.
const max = ([x, ...xs], result = -Infinity) => def(x) ? x > result ? max(xs, x) : max(xs, result) : result
Пример использования:
const array = [0,1,2,3,4,5] max(array) // 5
Факториал
Возвращает факториал числа. Использует аккумулятор, чтобы разрешить замену кадров стека, чтобы обеспечить возвращение больших факторов.
const factorial = (x, acum = 1) => x ? factorial(x - 1, x * acum) : acum
Пример использования:
factorial(5) // 120
Фибоначчи.
Возвращает номер Фибоначчи в данном месте.
const fib = x => x > 2 ? fib(x - 1) + fib(x - 2) : 1
Пример использования:
fib(15) // 610
QuickSort.
Сортировать массив от наименьшего до самого большого. Это делается путем повторного заказа массива, чтобы он содержит две подзарядки, один с меньшими значениями, другой с большим значениями. Вышеуказанные шаги рекурсивно применяются в каждом подпараситете до тех пор, пока не осталось никаких массивов, что сглаживается для возврата отсортированного массива.
const quicksort = (xs) => length(xs) ? flatten([ quicksort(filter(tail(xs), x => x <= head(xs))), head(xs), quicksort(filter(tail(xs), x => x > head(xs))) ]) : []
Это также может быть реализовано с использованием Раздел , но требуется переменное назначение.
const quicksort = (array) => { if (!length(array)) return [] const [less, more] = partition(tail(array), x => x < head(array)) return flatten([quicksort(less), head(array), quicksort(more)]) }
Пример использования:
const array = [8,2,6,4,1] quicksort(array) // [1,2,4,6,8]
Все как сокращение
Многие из функций выше могут быть преобразованы в сокращения, которые следует больше повысить производительность, если не все случаи. Это также показывает гибкость Уменьшить функция.
const reduce = ([x, ...xs], f, memo, i = 0) => def(x) ? reduce(xs, f, f(memo, x, i), i + 1) : memo const reverse = xs => reduce(xs, (memo, x) => [x, ...memo], []) const length = xs => reduce(xs, (memo, x) => memo + 1, 0) const map = (xs, fn) => reduce(xs, (memo, x) => [...memo, fn(x)], []) const filter = (xs, fn) => reduce(xs, (memo, x) => fn(x) ? [...memo, x] : [...memo], []) const reject = (xs, fn) => reduce(xs, (memo, x) => fn(x) ? [...memo] : [...memo, x], []) const first = (xs, n) => reduce(xs, (memo, x, i) => i < n ? [...memo, x] : [...memo], []) const last = (xs, n) => reduce(xs, (memo, x, i) => i >= (length(xs) - n) ? [...memo, x] : [...memo], []) const merge = spreadArg(xs => reduce(xs, (memo, x) => [...memo, ...x], [])) const flatten = xs => reduce(xs, (memo, x) => x ? isArray(x) ? [...memo, ...flatten(x)] : [...memo, x] : [], []) const add = spreadArg(([x, ...xs]) => reduce(xs, (memo, y) => memo + y, x)) const divide = spreadArg(([x, ...xs]) => reduce(xs, (memo, y) => memo / y, x)) const multiply = spreadArg(([x, ...xs]) => reduce(xs, (memo, y) => memo * y, x))
Пример использования:
reverse([1,2,3]) // [3,2,1] length([1,2,3]) // 3 map([1,2,3], double) // [2,3,4] filter([1,2,3,4], even) // [2,4] reject([1,2,3,4], even) // [1,3] first([1,2,3,4], 3) // [1,2,3] last([1,2,3,4], 2) // [3,4] merge([1,2,3],[4,5,6]) // [1,2,3,4,5,6] flatten([1,[2,3,[4,[5,[[6]]]]]]) // [1,2,3,4,5,6] add(1,2,3,4,5) // 15 multiply(2,5,10) // 100 divide(100,2,5) // 10
Обертывание
Я надеюсь, что эта статья помогает пролить представление о некоторых из моделей, доступных с JavaScript и ES6. Многие проблемы, которые могут быть решены с итерацией/петлями, также могут быть решены функционально с помощью рекурсии. Я надеюсь, что эта статья также смогла показать вам гибкость функции уменьшения.
Если вам нравится этот пост и нужна помощь с проблемой или искала нанимать фрилансера, я доступен для реагирования, Redux и JavaScript!