Li recentemente alguns artigos (por exemplo, http://dailyjs.com/2012/09/14/functional-programming/ ) sobre os aspectos funcionais do Javascript e a relação entre Scheme e Javascript (o último foi influenciado pelo primeiro, que é uma linguagem funcional, enquanto os aspectos OO são herdados do Self, que é uma linguagem baseada em prototipagem).
No entanto, minha pergunta é mais específica: eu queria saber se existem métricas sobre o desempenho da recursão versus iteração em Javascript.
Eu sei que em algumas linguagens (onde por iteração de design funciona melhor) a diferença é mínima porque o intérprete / compilador converte recursão em iteração, no entanto, acho que provavelmente esse não é o caso do Javascript, pois é, pelo menos parcialmente, um funcional língua.
Respostas:
O JavaScript não executa a otimização da recursão da cauda ; portanto, se a sua recursão for muito profunda, você poderá obter um estouro da pilha de chamadas. A iteração não tem esses problemas. Se você acha que vai recorrer muito e realmente precisa de recursão (por exemplo, para preencher), substitua a recursão por sua própria pilha.
O desempenho da recursão é provavelmente pior do que o desempenho da iteração, porque chamadas e retornos de função exigem preservação e restauração do estado, enquanto a iteração simplesmente salta para outro ponto em uma função.
fonte
var stack = []; var factorial = function(n) { if(n === 0) { return 1 } else { stack[n-1] = n * factorial(n - 1); return stack[n-1]; } }
else
função nessa porelse if (stack[n-1]) { return stack[n-1]; } else
e você terá memorização . Quem escreveu esse código fatorial teve uma implementação incompleta (e provavelmente deveria ter usado emstack[n]
todos os lugares, em vez destack[n-1]
).Atualização : desde o ES2015, o JavaScript tem TCO ; portanto, parte do argumento abaixo não permanece mais.
Embora o Javascript não tenha otimização de chamada final, a recursão geralmente é o melhor caminho a percorrer. E sinceramente, exceto em casos extremos, você não terá estouros de pilha de chamadas.
O desempenho é algo a ser lembrado, mas também a otimização prematura. Se você acha que a recursão é mais elegante que a iteração, faça isso. Se esse for o seu gargalo (o que talvez nunca seja), você poderá substituí-lo por uma iteração feia. Mas na maioria das vezes, o gargalo está nas manipulações do DOM ou, geralmente, na E / S, não no próprio código.
A recursão é sempre mais elegante 1 .
1 : Opinião pessoal.
fonte
Eu também estava curioso sobre esse desempenho em javascript, então fiz alguns experimentos (embora em uma versão mais antiga do nó). Escrevi uma calculadora fatorial recursivamente versus iterações e a executei algumas vezes localmente. O resultado parecia bastante inclinado para a recursão com um imposto (esperado).
Código: https://github.com/j03m/trickyQuestions/blob/master/factorial.js
fonte
"use strict";
e ver se isso faz diferença. (Ele produzirájump
s em vez da sequência de chamadas padrão)Conforme solicitação do OP, eu irei entrar em contato (sem me fazer de bobo, espero: P)
Acho que todos concordamos que a recursão é apenas uma maneira mais elegante de codificar. Se bem feito, pode resultar em código mais sustentável, o que é IMHO tão importante (se não mais) que reduzir 0,0001ms.
No que diz respeito ao argumento de que o JS não executa a otimização de chamada de cauda: isso não é mais verdade, o uso do modo estrito do ECMA5 permite o TCO. Era algo que eu não estava muito feliz há um tempo, mas pelo menos agora eu sei por que
arguments.callee
lança erros no modo estrito. Conheço o link acima para um relatório de bug, mas o bug está definido como WONTFIX. Além disso, o TCO padrão está chegando: ECMA6 (dezembro de 2013).Instintivamente, e mantendo a natureza funcional do JS, eu diria que a recursão é o estilo de codificação mais eficiente em 99,99% do tempo. No entanto, Florian Margaine tem razão quando diz que é provável que o gargalo seja encontrado em outro lugar. Se você está manipulando o DOM, provavelmente está mais concentrado em escrever seu código da maneira mais sustentável possível. A API do DOM é o que é: lenta.
Eu acho que é quase impossível oferecer uma resposta definitiva sobre qual é a opção mais rápida. Ultimamente, muitos jspref que eu vi mostram que o mecanismo V8 do Chrome é ridiculamente rápido em algumas tarefas, que são 4x mais lentas no SpiderMonkey da FF e vice-versa. Os mecanismos JS modernos têm todos os tipos de truques nas mangas para otimizar seu código. Não sou especialista, mas sinto que a V8, por exemplo, é altamente otimizada para fechamentos (e recursão), enquanto o mecanismo JScript da MS não é. O SpiderMonkey costuma ter um desempenho melhor quando o DOM está envolvido ...
Em resumo: eu diria que técnica terá melhor desempenho, como sempre em JS, é quase impossível de prever.
fonte
Sem o modo estrito, o desempenho da iteração geralmente é um pouco mais rápido que a recursão ( além de fazer com que o JIT faça mais trabalho ). A otimização da recursão da cauda elimina essencialmente qualquer diferença perceptível porque transforma toda a sequência de chamadas em um salto.
Exemplo: Jsperf
Sugiro me preocupar muito mais com a clareza e a simplicidade do código quando se trata de escolher entre recursão e iteração.
fonte