Array.map
Мы все, наверное, знаем Array.map. . Он преобразует массив элементов в соответствии с заданной функцией.
double = (x) => x * 2; map(double, [1, 2, 3]); // [2, 4, 6]
Я всегда видел, что это реализовано вдоль этих линий:
map = (fn, arr) => {
const mappedArr = [];
for (let i = 0; i < arr.length; i++) {
let mapped = fn(arr[i]);
mappedArr.push(mapped);
}
return mappedArr;
};
Это видео выяснил меня на альтернативу Array.map реализация. Это от Jsconf 2014 года – путь, прежде чем я прыгнул на функциональный программирующий Bandwag.
Редактировать: Дэвид Цизк и Стивена Blackstone Пожалуйста, указали на краю и субоптимальные характеристики в отношении этого карта реализация. Я бы не советую кому-либо использовать это в настоящем приложении. Мое намерение о том, что мы ценим и учимся от этого спровоцированного мышления, рекурсивный подход. ?
Оригинальный пример в CoffeeScript, вот эквивалент JavaScript.
map = (fn, [head, ...tail]) => head === undefined ? [] : [fn(head), ...map(fn, tail)];
Вы можете использовать Дэвид Цизк вместо этого более безопасная реализация.
map = (_fn_, [_head_, ..._tail_]) _=>_ (
head === undefined && tail.length < 1
? []
: [fn(head), ...map(fn, tail)]
);
Использование Назначение разрезания ES6 Мы храним первый элемент массива в переменную голова . Тогда мы храним Все остальные элементы массива в хвост Отказ
Если голова это undefined Это означает, что у нас есть пустой массив, поэтому просто верните пустой массив. Мы сопоставлено ничего такого.
map(double, []); // []
Если голова не неопределенный Мы возвращаем новый массив с Fn (голова) как первый элемент. У нас сейчас сопоставлено Первый элемент массива. Рядом с этим Карта (FN, хвост) который звонит карта Опять же, на этот раз с одним менее элементом.
С карта Возвращает массив, мы используем ES6 Распространение синтаксиса Чтобы объединить его с [ГОЛОВА] Отказ
Давайте проведем это в отладчике. Вставьте это в систему JavaScript вашего браузера.
map = (fn, [head, ...tail]) => {
if (head === undefined) {
return [];
}
debugger;
return [fn(head), ...map(fn, tail)];
};
Теперь давайте Карта (двойной, [1, 2, 3]) Отказ
Мы видим наши местные переменные:
head: 1 tail: [2, 3] fn: double
Мы знаем Fn (голова) это 2 Отказ Это становится новым первым элементом массива. Тогда мы называем карта снова с FN и остальные элементы массива: хвост Отказ
Поэтому до начала карта Звоните даже возврат, мы продолжим звонить карта пока массив не будет опустошен. Как только массив пустой, голова будет undefined , позволяя нашему базовому делу запустить и закончить весь процесс.
На следующем пробеге голова это 2 и хвост это [3] .
С хвост еще не пусто, нажмите на следующую точку останова, чтобы позвонить карта опять таки.
голова это 3 и хвост является пустым массивом. В следующий раз эта функция работает, она заново в строке 3 и, наконец, вернет сопоставленный массив.
И вот наш конец результата:
Array.filter.
Array.Filter Возвращает новый массив на основе элементов, которые удовлетворяют данной функции предиката.
isEven = (x) => x % 2 === 0; filter(isEven, [1, 2, 3]); // [2]
Рассмотрим это рекурсивное решение:
filter = (pred, [head, ...tail]) =>
head === undefined
? []
: pred(head)
? [head, ...filter(pred, tail)]
: [...filter(pred, tail)];
Если карта Умел смысл, это будет легко.
Мы все еще захватываем первый элемент массива в переменной под названием голова , а остальные в отдельном массиве под названием хвост Отказ
И с одинаковым базовым случаем, если голова это undefined Верните пустой массив и закончите итерацию.
Но у нас есть еще одно условное утверждение: только поставить голова в новом массиве, если ПРЕД (голова) это правда потому что Фильтр Работает путем тестирования каждого элемента против функции предиката. Только когда предикат возвращается правда Добавляем ли мы этот элемент в новый массив.
Если ПРЕД (голова) не возвращается правда Просто позвоните Фильтр (ПРЕД, хвост) без голова .
Давайте быстро расширяемся и выходите через это в Chrome Console.
filter = (pred, [head, ...tail]) => {
if (head === undefined) return [];
if (pred(head)) {
debugger;
return [head, ...filter(pred, tail)];
}
debugger;
return [...filter(pred, tail)];
};
И искать цифры ≤ 10:
filter(x => x <= 10, [1, 10, 20]);
С нашего массива [1, 10, 20] , голова первый элемент, 1 и хвост это массив остальных: [10, 20] .
Тесты предиката, если х ≤ 10, так ПРЕД (1) Возвращает правда Отказ Вот почему мы остановились на линии 4 Отладчик утверждение.
Так как нынешний голова Прошло тест, это разрешено вступление в наш фильтрованный массив. Но мы не сделаем, поэтому мы называем Фильтр снова с тем же предикатом, а теперь хвост Отказ
Перейти к следующему Отладчик Отказ
Мы позвонили Фильтр с [10, 20] Итак, голова сейчас 10, а хвост это [20] . Так как дела хвост Получите меньше с каждой последовательной итерацией?
Мы находимся на линии 4 Отладчик еще раз, потому что из-за 10 ≤ 10. Перейти к следующей точке останова.
голова сейчас 20 и хвост пусто.
С 20> 10, Pred (голова) Возвращает ложь И наш фильтрованный массив не будет включать его. Мы позвоним Фильтр еще раз без голова Отказ
В следующий раз, однако, Фильтр залог на линии 2. Разрушение Пустой массив дает вам undefined Переменные. Продолжайте пройти эта точка останова, чтобы получить ваше возвращаемое значение.
Это выглядит правильно для меня!
Массив
Последнее, но не менее важное, Array.reduce отлично подходит для кипения массива до одного значения.
Вот мой наивный Уменьшить реализация:
reduce = (fn, acc, arr) => {
for (let i = 0; i < arr.length; i++) {
acc = fn(acc, arr[i]);
}
return acc;
};
И мы можем использовать это так:
add = (x, y) => x + y; reduce(add, 0, [1, 2, 3]); // 6
Вы получите тот же результат с этой рекурсивной реализацией:
reduce = (fn, acc, [head, ...tail]) => head === undefined ? acc : reduce(fn, fn(acc, head), tail);
Я считаю, что это намного легче читать, чем рекурсивные карта и Фильтр Отказ
Давайте пройдем через это в консоли браузера. Вот расширенная версия с Отладчик заявления:
reduce = (fn, acc, [head, ...tail]) => {
if (head === undefined) {
debugger;
return acc;
}
debugger;
return reduce(fn, fn(acc, head), tail);
};
Тогда мы назовем это в консоли:
add = (x, y) => x + y; reduce(add, 0, [1, 2, 3]);
1 раунд
Мы видим наши местные переменные:
ACC : наше первоначальное значение 0.
FN : наше Добавить функция
голова : первый элемент массива, 1
хвост : Другие элементы массива упакованы в отдельный Массив, [2, 3]
С голова не неопределенный Мы собираемся рекурсивно звонить Уменьшить , прохождение по его обязательным параметрам :
fn : Очевидно, что Добавить Функция снова?
сочность : Результат звонка Fn (ACC, голова) Отказ С ACC это 0 и голова это 1 , Добавить (0, 1) Возвращает 1 Отказ
хвост : Остальные элементы массива. Всегда используя хвост, мы продолжаем резать массив вниз, пока ничего не осталось!
Перейти к следующему Отладчик Отказ
Раунд 2
Локальные переменные:
сочность : Теперь это 1 потому что мы позвонили Уменьшить с Fn (ACC, голова) , который был Добавить (0, 1) в то время.
fn : Все еще Добавить Действительно
голова : Помните, как мы прошли предыдущие хвост к Уменьшить ? Теперь это разрушилось, с голова представляющий свой первый элемент, 2 Отказ
хвост : Влево только один элемент, поэтому 3 был упакован в массив все сам по себе.
Мы знаем следующий Уменьшить Вызов будет делать функцию, аккумулятор и массив. Мы можем оценить следующий набор параметров Использование консоли Отказ
Ожидайте, что эти значения на следующей точке останова.
3 раунда
Наши местные переменные, как и ожидалось. голова первый и единственный элемент – 3 Отказ
И наш массив имеет только один элемент влево, хвост Пусто! Это означает, что следующая точка останова будет нашим последним.
Давайте быстро оценим наши будущие локальные переменные:
Перейти к окончательной точке останова.
4 раунда
Проверьте это, мы остановились на линии 3 вместо линии 6 на этот раз! голова это неопределенный Итак, мы возвращаем финал, 6 Действительно Это появится, если вы перейдете к следующей точке останова.
Выглядит хорошо для меня! Большое спасибо за прочтение этого.
Оригинал: “https://www.freecodecamp.org/news/implement-array-map-with-recursion-35976d0325b2/”