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

Введение в функциональное программирование на JavaScript

Знакомство с основными концепциями функционального программирования на языке программирования JavaScript

  • Введение в функциональное программирование
  • Функции первого класса
    • Они могут быть назначены переменным
    • Их можно использовать в качестве аргумента для других функций
    • Они могут быть возвращены функциями
  • Функции Более Высокого Порядка
  • Декларативное программирование
    • Декларативный против Императивного
  • Неизменность
    • константа
    • Объект.назначить()
    • сцепление()
    • фильтр()
  • Чистота
  • Преобразования данных
    • Массив.карта()
    • Массив.уменьшить()
  • Рекурсия
  • Композиция
    • Составление в простом JS
    • Сочинение с помощью lodash

Введение в функциональное программирование

Функциональное программирование (FP) – это парадигма программирования с некоторыми особыми техниками.

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

Haskell, Clojure и Scala являются одними из самых популярных чисто функциональных языков программирования.

Популярными языками программирования, поддерживающими методы функционального программирования, являются JavaScript, Python, Ruby и многие другие.

Функциональное программирование не является новой концепцией, на самом деле его корни уходят в 1930-е годы, когда родилось лямбда-исчисление, и повлияло на многие языки программирования.

В последнее время FP набирает обороты, так что сейчас самое подходящее время узнать об этом.

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

Функции первого класса

В функциональном языке программирования функции являются гражданами первого класса.

Они могут быть назначены переменным

const f = (m) => console.log(m)
f('Test')

Поскольку функция может быть назначена переменной, они могут быть добавлены к объектам:

const obj = {
  f(m) {
    console.log(m)
  }
}
obj.f('Test')

а также к массивам:

const a = [
  m => console.log(m)
]
a[0]('Test')

Их можно использовать в качестве аргумента для других функций

const f = (m) => () => console.log(m)
const f2 = (f3) => f3()
f2(f('Test'))

Они могут быть возвращены функциями

const createF = () => {
  return (m) => console.log(m)
}
const f = createF()
f('Test')

Функции Более Высокого Порядка

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

Примеры в стандартной библиотеке JavaScript включают Массив.карта() , Массив.фильтр() и Array.reduce() , который мы увидим чуть позже.

Декларативное программирование

Возможно, вы слышали термин “декларативное программирование”.

Давайте рассмотрим этот термин в контексте.

Противоположностью декларативного является императивный .

Декларативный против Императивного

Императивный подход – это когда вы сообщаете машине (в общих чертах), какие шаги ей необходимо предпринять, чтобы выполнить работу.

Декларативный подход – это когда вы сообщаете машине, что вам нужно сделать, и позволяете ей разобраться в деталях.

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

Кто-то может возразить, что программирование на языке Си более декларативно, чем программирование на ассемблере, и это правда.

HTML является декларативным, поэтому, если вы используете HTML с 1995 года, вы фактически создаете декларативные пользовательские интерфейсы более 20 лет.

JavaScript может использовать как императивный, так и декларативный подход к программированию.

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

Неизменность

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

Переменная никогда не может быть изменена. Чтобы обновить ее значение, вы создаете новую переменную.

Вместо изменения массива, чтобы добавить новый элемент, вы создаете новый массив путем объединения старого массива и нового элемента.

Объект никогда не обновляется, но копируется перед его изменением.

константа

Вот почему ES2015 const так широко используется в современном JavaScript, который охватывает концепции функционального программирования: для обеспечения неизменности переменных.

Объект.назначить()

ES2015 также предоставил нам Object.assign(), который является ключом к созданию объектов:

const redObj = { color: 'red' }
const yellowObj = Object.assign({}, redObj, {color: 'yellow'})

сцепление()

Для добавления элемента в массив в JavaScript мы обычно используем метод push() для массива, но этот метод изменяет исходный массив, поэтому он не готов к FP.

Вместо этого мы используем метод concat() :

const a = [1, 2]
const b = [1, 2].concat(3)
// b = [1, 2, 3]

или мы используем оператор распространения:

const c = [...a, 3]
// c = [1, 2, 3]

фильтр()

То же самое относится и к удалению элемента из массива: вместо использования pop() и splice() , которые изменяют исходный массив, используют array.filter() :

const d = a.filter((v, k) => k < 1)
// d = [1]

Чистота

A чистая функция :

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

Преобразования данных

Поскольку неизменяемость является такой важной концепцией и основой функционального программирования, вы можете спросить, как могут изменяться данные.

Просто: данные изменяются путем создания копий .

Функции, в частности, изменяют данные, возвращая новые копии данных.

Основными функциями, которые делают это, являются map и уменьшить .

Массив.карта()

Вызов Array.map() для массива создаст новый массив с результатом функции, выполняемой для каждого элемента исходного массива:

const a = [1, 2, 3]
const b = a.map((v, k) => v * k)
// b = [0, 2, 6]

Массив.уменьшить()

Вызов Array.reduce() для массива позволяет нам преобразовать этот массив во что угодно другое, включая скаляр, функцию, логическое значение, объект.

Вы передаете функцию, которая обрабатывает результат, и отправную точку:

const a = [1, 2, 3]
const sum = a.reduce((partial, v) => partial + v, 0)
// sum = 6
const o = a.reduce((obj, k) => { obj[k] = k; return obj }, {})
// o = {1: 1, 2: 2, 3: 3}

Рекурсия

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

Классическим примером рекурсии является вычисление последовательности Фибоначчи (N = (N-1 + N-2)), здесь в ее 2 ^N совершенно неэффективном (но приятном для чтения) решении:

var f = (n) => n <= 1 ? 1 : f(n-1) + f(n-2)

Композиция

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

Композиция – это то, как мы генерируем функцию более высокого порядка, комбинируя более простые функции .

Составление в простом JS

Очень распространенный способ создания функций в обычном JavaScript – это их объединение в цепочку:

obj.doSomething()
   .doSomethingElse()

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

obj.doSomething(doThis())

Сочинение с помощью lodash

В более общем плане составление – это процесс составления списка из множества функций для выполнения более сложной операции.

lodash/fp поставляется с реализацией compose : мы выполняем список функций, начиная с аргумента, каждая функция наследует аргумент от возвращаемого значения предыдущей функции . Обратите внимание, что нам нигде не нужно хранить промежуточные значения.

import { compose } from 'lodash/fp'

const slugify = compose(
  encodeURIComponent,
  join('-'),
  map(toLowerCase),
  split(' ')
)

slufigy('Hello World') // hello-world

Оригинал: “https://flaviocopes.com/javascript-functional-programming/”