Цикл или рекурсия
Из текста учебника я понял, что код с использованием цикла эффективнее по сравнению с рекурсией. Но...
Я нашел в интернете функцию, которая из введенного в текстовое поле списка строк удаляет дубликаты. Мне нужна более специфическая задача, но очень похожая, и я решил взять за основу интернетный прототип. Он был реализован в виде рекурсии. Я сделал с циклом и он работает в разы быстрее, но меня терзают смутные сомнения. Ведь почему-то изначально программист (видимо более опытный чем я) сделал с рекурсией... |
Не парься. Любой рекурсивный алгоритм выразим итеративно. В языках без оптимизации хвостовой рекурсии (ЕМНИП, js к таковым относиться), рекурсия -- это жопа. Она память жрет, производительность падает. Не нужно рекурсивно ничего пейсать. Некоторые хомячки пишут рекурсивно, просто ради позерства, чтобы показать
Нутыпонел. |
Цитата:
Цитата:
|
Цитата:
Цитата:
|
Цитата:
|
Цитата:
|
Цитата:
|
Цитата:
|
Цитата:
Без рекурсии: https://github.com/kobezzza/Collecti...e/obj.jsn#L118 изучай. *** Сколько лет ты пишешь код? 0.1? оно и видно, но зато в каждом треде всех учишь и рассказываешь сказки. |
Цитата:
Ты уверен, что ты понимаешь о чем говоришь? Код:
fact=function(n){if(n<2) return 1; return n*(fact(n-1))} |
Цитата:
|
Цитата:
|
Цитата:
|
foo, напиши реализация. Я дал тебе сссылку с пруфом, так что либо давай код, либо прекрати пиздеть и уёбывай.
|
Ты несешь хуету. Пиздишь тут ты а не я. Ты может мастер по напейсанию js-сахарка, но в программировании ты дуб, и ты не понимаешь, что такое хвостовая рекурсия, ты слился в говно, и еще ебальник разеваешь при этом. А писать и перпеписывать я буду то что считаю нужным, есличо.
|
Цитата:
|
foo слив засчитан.
|
Цитата:
ибо нехуй форум портитьб! |
Цитата:
|
Цитата:
Только я не понимаю, чёйт ты ушёл в оптмизации, если ты утверждаешь, что рекурсия не выразительная и не нужно поэтому её юзать.
removeFolder(cat) {
for (var i = 0; i < cat.length; i++) {
if (isFile(cat[i])) {
unlink(cat[i]);
} else {
removeFolder(cat[i]);
}
}
rmdir(cat);
}
Терь давай перепиши на циклы и докажи, что вариант на циклах очевиднее. |
Цитата:
|
Цитата:
|
Цитата:
|
Цитата:
removeFolder(cat) {
for (var i = 0; i < cat.length; i++) {
if (isFile(cat[i])) {
unlink(cat[i]);
} else {
removeFolder(cat[i]);
}
}
rmdir(cat);
}
removeFolder(cat[i]); А это что? Я начинаю подозревать, что ты идиот (хотя стоп, я в этом уверен). Цитата:
|
Цитата:
|
Цитата:
зы код пока не смотрел. |
Цитата:
|
Цитата:
|
kobezzza,
короче, не буду лить воды. Я проверил в ноде на следующем коде: Код:
Попробуй на своей модной новейшей ноде, потом расскажешь. А пока я засчитываю тебе слив. В JS нет оптимизации хвостовой рекурсии. |
Цитата:
Цитата:
|
Цитата:
Цитата:
|
можно попробовать перевести код функции к циклу через конструкцию с throw и try-catch
try будет ловить исключение, выбрасываемое, когда стек переполнен, а catch будет менять значение аргументов, ибо они передаются в объекте исключения. и так бесконечно, пока не выйдем из функции бредовый пример :) правда чутка мимо кассы функция
function foo (from) {
return from ? foo ( document.body.innerHTML = --from ) : from;
}
вызываем foo(1e6); JSBIN ---> http://jsbin.com/rojeguze/1 и её взломанный вариант
function foo (from) {
function foo () {
throw arguments;
}
while (true) {
try {
// START
return from ? foo ( document.body.innerHTML = --from ) : from;
// END
} catch (e) {
from = e[0];
}
}
}
Осторожно! Firefox замерзает на несколько секунд. JSBIN ---> http://jsbin.com/ridibaye/1 |
Цитата:
В современных VM JIT компиляторы неплохо оптимизируют такие виды рекурсий (уж не знаю что они там делают) и часто получается, что рекурсивная форма работает также быстро как и pure цикл, но ограничение стека по прежнему есть. Пожалуй я не правильно выразил свою мысль, когда говорил про оптимизацию. (Однако следует заметить, что настоящая оптимизация хвостовой рекурсии добавлена в стандарт ES6 и будет работать с флагом 'use strict') *** Я всё жду когда ты мне дашь код паттерна "примесь" на циклах и докажешь, что та простыня кода очевиднее и лучше рекурсии :) Только не надо рассказывать, что "раз мы юзаем рекурсии, то не юзаем циклы". Для любой задачи поиска в глубину рекурсия самое простое, короткое и очевидное решение. И вообще я спорю не про то, что рекурсия лучше или быстрее цикла (очевидно, что цикл потенциально позволит написать более оптимальный код), а про то, что есть пласт задач, которые рекурсивно решаются очень просто, а при решении одними циклами, то нам придётся городить лес дополнительных инструкций и в большинстве случаев это лишнее. Но при этом я не отказываюсь от циклов, а юзаю всё вместе, когда это удобно и делает код нагляднее, а язык позволяет так делать. Пример я уже приводил:
removeFolder(cat) {
var dir = readdir(cat);
for (var i = 0; i < dir.length; i++) {
if (isFile(dir[i])) {
unlink(dir[i]);
} else {
removeFolder(dir[i]);
}
}
rmdir(cat);
}
Мне трудно представить ситуацию, когда количество вложенных папок приведёт к переполнению стека и придётся этот код переписывать :) |
Цитата:
Я начал читать твой код, и, попутно вопрос возник. Ты там клонируешь объект Код:
Collection.clone = function (obj) { |
Collection.clone - это служебный метод для быстрого клонирования JSON валидных объектов, т.е. это частный случай и по сути хакерство, но работает для своих задач очень быстро.
Настоящие клонирования может сделать extend (тут уже я переписал на рекурсию с циклов, т.к. в последних VM профита уже не стало, а код стал реально понятнее и проще).
// Полное клонирование объекта a
var b = Collection.extend({deep: true, withProto: true, withAccessors: true}, {}, a)
Тут тебе и клонирование прототипов, аксессоров, штрихи, примеси и т.д. |
kobezzza,
А там у тебя let используется. Это же не часть стандарта насколько я понял? Зачем ты используешь let? чем тебя не устраивают var? Это же тоже самое, насколько я понимаю? Или есть разница? |
let - это часть ES6. И отличается он от var тем, что объявляет переменные с блочной областью видимости.
|
Вот для сравнения сейчас накалякал на коленке: функция удаления непустой папки (которая может содержать вложенные папки и т.д.)
С рекурсией: - 13 строк
function removeFolder(cat) {
var dir = readdir(cat);
for (var i = 0; i < dir.length; i++) {
if (isFile(dir[i])) {
unlink(dir[i]);
} else {
removeFolder(dir[i]);
}
}
rmdir(cat);
}
Без рекурсии: - 35 строк
function removeFolder(cat) {
var dir = readdir(cat),
stack = [];
for (var i = 0; i < dir.length; i++) {
if (isFile(dir[i])) {
unlink(dir[i]);
} else {
stack.push({
i: i,
dir: dir,
cat: cat
});
cat = dir[i];
dir = readdir(cat);
i = -1;
continue;
}
if (i === dir.length - 1 && stack.length) {
rmdir(cat);
var last = stack.pop();
cat = last.cat;
dir = last.dir;
i = last.i;
}
}
rmdir(cat);
}
Вариант на одних циклах в 2.5 раза больше, понять его сложнее и сложнее модифицировать. Мне кажется это самоочевидно, я даже не понимаю о чём тут можно спорить. Конечно, когда у нас хвостовая рекурсия, то её с оптимизировать можно проще, т.к. не нужно создавать стек, но это уже частный случай. |
Цитата:
let - это переменная с блочной видимостью, а var с функциональной.
if (1) {
let a = 1;
}
a // a is not defined
|
Цитата:
|
| Часовой пояс GMT +3, время: 18:06. |