Коротка відповідь
juniorФункція reduce за допомогою рекурсії працює, перебираючи масив крок за кроком і акумулюючи результат. При кожному виклику передається поточний індекс і накопичене значення, а рекурсія завершує, коли індекс перевищує довжину масиву. Приклад: `const r = (arr, fn, acc, i=0)=>i===arr.length?acc:r(arr,fn,fn(acc,arr[i]),i+1);`
Повне пояснення
Що це і навіщо
Рекурсивний reduce дозволяє обчислити одне значення з масиву без використання циклів, що корисно в функціональному стилі.
Принцип роботи / алгоритм
- При першому виклику функція приймає масив, callback, початковий акумулятор і індекс (за замовчуванням 0).
- Якщо
i === arr.length, повертаємо акумулятор – це базовий випадок. - Інакше викликаємо
fn(acc, arr[i]), отримуємо новий акумулятор і рекурсивно викликаємоrз індексомi+1.
Реалізація (JS / TS)
function r(arr, fn, acc, i = 0) {
return i === arr.length ? acc : r(arr, fn, fn(acc, arr[i]), i + 1);
}
// Використання
const sum = r([1,2,3], (a,b)=>a+b, 0); // 6
Тестування
import { test, expect } from 'vitest';
test('reduce sum', () => {
const res = r([1,2,3], (a,b)=>a+b, 0);
expect(res).toBe(6);
});
Проблеми та edge cases
- Глибина рекурсії – для великих масивів перевищує стек, використати tail‑call оптимізацію або цикл.
- Порожній масив – повертає
accбез виклику callback. - Відсутність початкового значення – треба обробити, наприклад, взяти
arr[0]якacc. - Невірний тип callback – перевірити, що це функція.
- Масив з
undefined– callback повинен коректно обробляти значення.
Оптимізація та продуктивність
Tail‑call оптимізація (ES6) дозволяє уникнути збільшення стеку, але не підтримується в усіх браузерах. Для продуктивності краще використовувати цикл.
Без JS / архітектурні альтернативи
У чистому HTML/CSS немає еквіваленту reduce. Можна обчислити значення на сервері або в шаблоні.
Cheatsheet + follow-up
- Tail‑call:
function f(i){ if(i===n)return; return f(i+1); } - Питання: Як обробити масив з
nullзначеннями? Чому рекурсія не підходить для великих масивів?