Eu tenho aprendido F # e está começando a influenciar como eu penso quando estou programando C #. Para esse fim, uso a recursão quando sinto que o resultado melhora a legibilidade e não consigo imaginá-lo acabar com um estouro de pilha.
Isso me leva a perguntar se os compiladores poderiam ou não converter automaticamente funções recursivas em um formato não recursivo equivalente?
programming-languages
compiler
recursion
Anodeto de Aaron
fonte
fonte
return recursecall(args);
para a recursividade, o material mais complexo é possível através da criação de uma pilha explícita e enrolando-a para baixo, mas eu duvido que eles vãoRespostas:
Sim, alguns idiomas e complementadores converterão a lógica recursiva em lógica não recursiva. Isso é conhecido como otimização de chamada final - observe que nem todas as chamadas recursivas são otimizadas para chamada final. Nessa situação, o compilador reconhece uma função do formulário:
Aqui, o idioma é capaz de reconhecer que o resultado retornado é o resultado de outra função e alterar uma chamada de função com um novo quadro de pilha em um salto.
Perceba que o método fatorial clássico:
não é otimizado para chamada final devido à inspeção necessária no retorno.
Para tornar essa chamada final otimizável,
Compilando esse código com
gcc -O2 -S fact.c
(o -O2 é necessário para ativar a otimização no compilador, mas com mais otimizações de -O3 fica difícil para um ser humano ler ...)Pode-se ver no segmento
.L4
, emjne
vez de umcall
(que faz uma chamada de sub-rotina com um novo quadro de pilha).Observe que isso foi feito com C. A otimização de chamada de cauda em java é difícil e depende da implementação da JVM - tail-recursion + java e tail-recursion + optimization são bons conjuntos de tags para navegar. Você pode achar que outros idiomas da JVM são capazes de otimizar melhor a recursão da cauda (tente o clojure (que requer que a repetição para otimizar a chamada da cauda) ou scala).
fonte
Pisar com cuidado.
A resposta é sim, mas nem sempre, e nem todos eles. Esta é uma técnica que usa alguns nomes diferentes, mas você pode encontrar algumas informações bastante definitivas aqui e na wikipedia .
Prefiro o nome "Otimização de chamada de cauda", mas há outros e algumas pessoas confundem o termo.
Dito isto, há algumas coisas importantes a serem realizadas:
Para otimizar uma chamada final, a chamada final requer parâmetros conhecidos no momento em que a chamada é feita. Isso significa que, se um dos parâmetros for uma chamada para a própria função, ela não poderá ser convertida em um loop, porque isso exigiria o aninhamento arbitrário do referido loop, que não pode ser expandido no tempo de compilação.
O C # não otimiza confiavelmente as chamadas de cauda. O IL tem a instrução de fazer isso que o compilador F # emitirá, mas o compilador C # emitirá inconsistentemente e, dependendo da situação do JIT, o JIT pode ou não fazê-lo. Todas as indicações são de que você não deve confiar na otimização de suas chamadas finais em C #, o risco de transbordamento ao fazê-lo é significativo e real
fonte