Проблем с кодированием и веб-сайтов предостаточно, и мне нравится решать новые проблемы, но иногда интересно настроить или улучшить старые игрушечные проблемы и придумать альтернативные творческие решения. Особенно здорово, если вы можете сделать это с еще меньшим количеством кода. Один из способов, которым я люблю бросать себе вызов, — это решать задачи с помощью рекурсии, не всегда потому, что это лучше, а просто потому, что мне нравится рекурсия. Это может привести к трудностям, потому что некоторые задачи более эффективны при использовании простого цикла while, но рекурсия все равно доставляет удовольствие. Еще одна распространенная проблема, которую я себе задаю, — это использование в решении «reduce» или «reduceRight». Дополнительный уровень абстракции почти всегда более интересен, чем стандартные циклы.
Недавно друг рассказал мне о головоломке рефакторинга, которая включает в себя обе мои любимые дополнительные задачи. Его попросили заново реализовать «reduce» в JavaScript, используя рекурсию! В довершение всего ему сказали сделать это в две строки. Что ж, мне не терпелось решить эту проблему, и ниже я разберу ее.
var reduce = function(collection, callback, startVal){ var initVal = arguments.length > 2; collection.forEach(function(element){ if(!initVal){ startVal = element; initVal = true; }else{ startVal = callback(startVal, element); } }); return startVal; console.log(reduce([1,2,3,4,5], function(a,b){return a+b;}, 1)); // 16 };
Выше вы можете увидеть базовую реализацию сокращения. Функция проверяет, передано ли начальное значение в вызов, используя метод «.length» для объекта arguments. Если нет, начальное значение устанавливается на первый элемент в коллекции. Предоставленный обратный вызов вызывается для начального значения и каждого элемента коллекции. «startVal» переназначается возвращаемому значению обратного вызова на каждой итерации. Наконец, startVal возвращается после того, как каждый элемент в коллекции был уменьшен или свернут с помощью предоставленного обратного вызова. Теперь давайте попробуем провести рефакторинг, чтобы это было всего две строки кода и использовалась рекурсия. Для этой задачи я также буду использовать немного ES6!
let reduce = (collection, callback, startVal = 0) => { if(collection.length === 0) return startVal; return reduce(collection, callback, callback(startVal, collection.shift())); }; console.log(reduce([1,2,3,4,5], function(a,b){return a+b;}, 1)); // 16
Тело этого решения состоит всего из двух строк кода! Мы удаляем тест начальных аргументов, используя новые «значения по умолчанию» ES6 в параметре. (Это не так полно, потому что по умолчанию он равен нулю вместо первого элемента в коллекции, но он будет работать для массива чисел) Теперь функция может начинаться с базового случая, который завершает рекурсию, когда длина коллекции равен нулю. Затем функция редукции вызывается рекурсивно, передавая коллекцию, обратный вызов и магию редукции. startVal переназначается возвращаемому значению обратного вызова в рамках рекурсивного вызова. «startVal» из предыдущего вызова является первым параметром в вызове обратного вызова, и с помощью метода .shift второму параметру присваивается первый элемент в массиве. Поскольку массивы находятся в объектах памяти, при вызове .shift массив укорачивается на единицу и перемещается на один шаг ближе к базовому варианту!
Вот и все, уменьшите в две строки, используя рекурсию и ES6! Какие еще забавные рефакторинги возможны?