Почему я влюбился в функциональное программирование

Поскольку сейчас я работаю как фронтенд-разработчик Javascript на полную ставку, я изо дня в день работаю с такими библиотеками, как React и Redux. Примеры из Интернета и передовые методы работы с этими библиотеками в основном демонстрируют программирование в функциональном стиле.

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

Если бы я описал это словами, в императивном программировании вы описываете, как выполнять желаемое действие. В декларативном программировании вы описываете, какое действие выполнять.

Позвольте мне проиллюстрировать это на простом примере (я буду использовать синтаксис ES6):

Допустим, у вас есть массив чисел и вы хотите умножить каждый элемент на два. Вот как вы это делаете в императивном стиле:

Это то, что вы увидите, когда кто-то привык писать код на C ++ / Java. Конечно, вы также можете использовать функцию forEach (значение, индекс).

Однако декларативный стиль выглядел бы иначе:

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

Вы также можете указать второй аргумент, которым является индекс элемента array.map ((значение, индекс) = ›{return array [index]}). В данном случае он нам не нужен, поэтому мы его опускаем. Полная спецификация здесь https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map.

На первый взгляд это не кажется большим преимуществом, но давайте попробуем более сложный пример. Я возьму урок с сайта CodeWars (https://www.codewars.com/), который я обнаружил недавно и начал использовать его для тренировки своих навыков с помощью различных упражнений.

Вот один пример урока

Возьмите число и просуммируйте его цифры в порядке следования степеней и… .¡Eureka !!

Число 89 - это первое целое число с более чем одной цифрой, которое соответствует свойству, частично введенному в заголовке этого ката. Что толку говорить «Эврика»? Потому что эта сумма дает столько же.

Действует: 89 = 8^1 + 9^2

Следующее число в наличии этого свойства - 135.

Посмотрите это свойство еще раз: 135 = 1^1 + 3^2 + 5^3

Нам нужна функция для сбора этих чисел, которая может принимать два целых числа a, b, которые определяют диапазон [a, b] (включительно) и выводят список отсортированных чисел в диапазоне, который выполняет свойство, описанное выше.

Давайте посмотрим на некоторые случаи:

sumDigPow(1, 10) == [1, 2, 3, 4, 5, 6, 7, 8, 9]

sumDigPow(1, 100) == [1, 2, 3, 4, 5, 6, 7, 8, 9, 89]

Если таких чисел в диапазоне [a, b] нет, функция должна вывести пустой список.

sumDigPow(90, 100) == []

Позвольте мне сначала кратко описать, как реализовать такой алгоритм. Во-первых, вам нужно перейти от a к b.

Затем вы преобразуете это число в строку, разделите его на отдельные буквы и снова преобразуете каждую букву в число (135 = ›« 135 = ›[« 1 »,« 3 »,« 5 »] =› [1, 3, 5 ]).

Затем вы должны привести отдельные числа в соответствие с их индексом + 1 в массиве и, наконец, просуммировать все значения ([1, 3, 5] = ›[1¹, 3², 5³] =› 1 + 9 + 125).

И, наконец, сравните его с исходным целым числом (135 равно 135?) И добавьте его в окончательный массив, если эти два числа равны. Вот как это может выглядеть в императивном стиле:

Это симпатичная эффективная императивная реализация алгоритма. Однако, когда вы смотрите на это, довольно сложно понять, что происходит в этом коде.

Теперь попробуем реализовать это декларативно:

Поясним реализацию.

Сначала мы создаем массив от 0 до 150 ([0, 1, 2,… 149, 150]), который мы будем перебирать позже.

Затем мы создаем функцию calc, которая выполняет действия в следующем порядке:

а) Преобразование числа в строку - String ()
б) Разделение строки на отдельные буквы - split ('') )
c) Преобразование всех букв в массиве в числа - map (Number)
d) Мощность каждого числа показатель степени индекса + 1 - map ((n, i) = ›Math.pow (n, i + 1))
e) Сумма созданного массива с использованием функция уменьшения. Вы можете узнать больше о функции уменьшения здесь https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce

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

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

(convert to)String => Split => (convert to)Number => (calculate)Pow

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

Если вы хотите узнать больше о функциональном программировании на Javascript, обязательно ознакомьтесь с функциями массива map, filter и reduce. Определенно есть чему поучиться, но это хорошее начало.

Я также рекомендую попробовать упражнения CodeWars (https://www.codewars.com/), чтобы получить больше навыков.

Я ищу больше блогов / веб-сайтов, посвященных функциональному программированию на Javascript. Не стесняйтесь делиться своим любимым в комментариях.

Спасибо за чтение! :) Если вам понравилось, нажмите кнопку с сердечком внизу. Для меня это будет значить мир, и это поможет другим людям увидеть историю.