Элементы функционального программирования
Функциональное программирование(FP) — способ организации кода через написание набора функций.
EcmaScript, являясь мультипарадигменным языком программирования, реализует наряду с прочими и функциональную парадигму. Это означает, что функции в ES являются данными и могут быть переданы в функции, возвращены из функций и могут сами принимать функции. Т.е. функции в ES являются функциями первого класса.
Отсюда следуют следующие определения:
Функциональный агрумент — аргумент, значением которого является функция.
Функция высшего порядка — функция, которая принимает функции в качестве аргументов.
Функции с функциональным значением — функция, которая возвращает функцию.
Давайте на примере посмотрим что к чему.
Мы же знаем, что функция triple
вернет другую функцию, которую мы сохранили в переменную triple_bar
. А значит что теперь мы можем функцию triple_bar
вызвать:
Рекурсия
Рекурсия — определение, описание, изображение какого-либо объекта или процесса внутри самого этого объекта или процесса, то есть ситуация, когда объект является частью самого себя. Термин «рекурсия» используется в различных специальных областях знаний — от лингвистики до логики, но наиболее применение находит в математике и информатике.
Применительно к программированию под рекурсией подразумевают процессы, которые вызывают сами себя в своём теле. Рекурсивная функция имеет несколько обязательных составляющих:
- Терминальное условие — условие прекращения выполнения
- Правило по которому осуществляется движение в глубь по рекурсии
Рассмотрим самый популярный пример рекурсии — вычисление факториала.
Выделим характерные составляющие рекурсивной функции. Терминальное условие
и правило движения по рекурсии
Рекурсия как замена циклу
Вообще мы могли бы даже отказаться от циклов и реализовывать их через рекурсии. Обратите внимание на то, что рекурсиии и циклы похожи по 2 пунктам:
- Цикл повторяет какие-то действия пока выполняется условие, у рекурсии если правило перехода на следующую итерацию, так называемое правило движения по рекурсии.
- Цикл прекращает свою работу при определенном условии (а точнее - при ситуации когда условие не выполняется). С рекурсией что-то похожее.. что называется терминальным условием.
Давайте не будем мусолить и посмотрим на замену следующего цикла c помощью рекурсии. Вот сам цикл:
Что мы здесь видим? Ну то, что "терминальное условие цикла" - это i >= 3
. Значит мы можем легко переписать пример через рекурсию:
Вуаля👏👏👏! Если вы прогоните этот код, то результат и там и там будет аналогичным.
Обход дерева
Рекурсию удобно использовать для работы с рекурсивными структурами данных, такими как вложенные списки или деревья.
note
Обратите внимание на определение рекурсии, для того чтобы стало понятнее что такое рекурсивные структуры данных.
Представим следующее дерево:
Да что это за деревья!😡⁉️
Дерево — одна из наиболее широко распространённых структур данных в информатике, эмулирующая древовидную структуру в виде набора связанных узлов. Является связным графом, не содержащим циклы. Большинство источников также добавляют условие на то, что рёбра графа не должны быть ориентированными. В дополнение к этим трём ограничениям, в некоторых источниках указывается, что рёбра графа не должны быть взвешенными.
Как мы понимаем вложенность папок и файлов может быть сколь угодно глубокой. А как нам обработать такую структуру, например вытащить все названия папок? Конечно, мы можем выдумывать с вами алгоритм на циклах(итеративный), но зачем усложнять жизнь? Давайте накинем функцию которая может вытаскивать список файлов из папки:
Что мы сейчас получим если применим функцию getFilesFromFolder
к папке в переменной FS_TREE
?
В ответ мы получим примерно такой объект:
Нам сейчас останется переписать функцию таким образом чтобы она не просто фильтровала элементы в свойстве children
и возвращала только файлы а еще применяла саму себя к тем папкам которые находятся в свойстве children
.
Популярные функции высшего порядка
Напоминаю, что функции высшего порядка это те функции которые получают другие функции как свои аргумент или возвращают функции. На постоянной основе вы будете пользоваться HOF-методами массивов: Array.prototype.map()
, Array.prototype.filter()
, Array.prototype.find()
, Array.prototype.forEach()
и Array.prototype.reduce()
.
Array.prototype.map()
Эта функция высшего порядка позволяет нам применить какую-либо функцию к каждому элементу массива и получить массив результатов применения этой функции.
Давайте посмотрим на достаточно простой пример, у нас есть массив чисел:
нам по некоторым причинам требутеся умножить каждый элемент на 2, для этого мы напишем функцию:
и применем ее к каждому элементу в NUMS
:
Array.prototype.filter()
Для сортировки массива мы можем воспользоваться тем же циклом.. например для получения всех элементов из массива которые больше чем 100, мы напишем что-то такое:
Но можно это сделать чуть более элегантно с применением метода массива filter
, который получает тест-функцию, которая в свою очередь уже говорит, элемент который она получила проходит проверку или нет.
После написания фунции мы можем передать ее в метод filter
: