|
Рекурсивные функции
Начиная с этого занятия, мы немного
глубже рассмотрим работу с функциями в JavaScript и начнем с
понятия рекурсии.
Рекурсивные функции – это функции,
которые вызывают сами себя.
Понять работу рекурсии проще всего на примере.
Предположим, мы хотим вычислить значение числа x в степени n. Для простоты
степень n у нас будет
натуральным числом: n = 1, 2, 3,… Один из вариантов реализации может быть
такой:
function pow(x, n) {
let res = 1;
for(let i = 0;i < n;++i)
res *= x;
return res;
}
Вызываем функцию, проверяем как она
работает:
console.log( pow(2, 2) );
Реализуем эту же функцию через рекурсию.
Для этого заметим, что
то есть, для вычисления значения на
текущем n-м шаге
достаточно взять значение на предыдущем n-1-м шаге и
умножить его на x. Эта формула, по сути, отражает принцип рекурсии. В
нашем случае она будет выглядеть так:
x*pow(x,n-1)
Получаем такую реализацию:
function pow(x, n) {
if(n <= 0) return 1;
else return x*pow(x, n-1);
}
let res = pow(2, 3);
Как это работает? Сначала вызывается функция
pow(2, 3). Она
помещается в стек вызова функций, в котором хранится контекст выполнения (execution
context) текущей функции pow(2, 3). Далее, выполняется тело функции.
Проверяется первое условие. Оно оказывается ложным, так как 3 <= 0 дает false. Поэтому идет
переход на else и прежде чем
выполнить оператор return, снова вызывается та же функция pow(2, 2).
Выполнение функции pow(2, 3) останавливается,
в стек помещается контекст выполнения второй функции pow(2, 2) и она
запускается. Снова проверяется первое условие, оно ложно, переходим по else к оператору return и опять вызываем
функцию pow(2, 1).
Здесь снова все повторяется, в стек
помещается очередной вызов функции и по условию вызывается следующая функция pow(2, 0).
Теперь первое условие становится
истинным и функция pow(2,0) возвращает значение 1 и рекурсия не идет
дальше вглубь – она останавливается. Функция pow(2.0)
завершается, ее контекст выполнения удаляется из стека вызовов и управление
передается функции pow(2, 1). Но она частично уже выполнена. Поэтому,
берется значение 1 от pow(2, 0), результат умножается на x=2 и величина 2
возвращается функцией pow(2, 1).
Контекст выполнения функции pow(2,1) удаляется
из стека, управление переходит к вышестоящей функции pow(2,2) и здесь мы
уже имеем результат умножения x*x, то есть, 2*2 =
4. Далее, возвращаемся к самой верхней функции pow(2,3), здесь 2*4=8
и этот результат становится результатом вычисления рекурсивной функции.
Рекурсию очень удобно применять для
обхода свойств объекта сложной структуры. Представьте, что у нас есть компания,
состоящая из нескольких отделов и в каждом отделе работают свои сотрудники:
let company = {
sales: [{name: 'Иван', salary: 1000}, {name: 'Михаил', salary: 600}],
development: {
sites: [{name: 'Евгений', salary: 2000}, {name: 'Алексей', salary: 1800}],
internals: [{name: 'Федор', salary: 1300}]
}
};
И нужно написать функцию вычисления
суммарной зарплаты для всей компании и для каждого из отделов. Это можно реализовать так:
function sumSalary(department) {
if(Array.isArray(department)) {
return department.reduce((prev, current) => prev + current.salary, 0);
}
else {
let sum = 0;
for(let prop in department) {
sum += sumSalary(department[prop]);
}
return sum;
}
}
И вызывать для любого отдела или
компании в целом:
console.log( sumSalary(company) );
console.log( sumSalary(company.sales) );
console.log( sumSalary(company.development) );
Вот что из себя представляют рекурсивные функции. У
них есть только одно важное ограничение: стек вызова не бесконечный – нельзя
рекурсию делать слишком глубокой. Разумное значение – не более 1000 рекурсивных
итераций. Если требуется больше, то алгоритм лучше подкорректировать.
|