Почему я влюбился в функциональное программирование
Поскольку сейчас я работаю как фронтенд-разработчик 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. Не стесняйтесь делиться своим любимым в комментариях.