Qual é a recursão na programação e como ela é usada?

A recursão é uma parte importante da programação funcional que pode ajudá-lo a resolver problemas complexos com soluções elegantes. No entanto, é importante entender os prós e contras para fazê-lo corretamente.

RELACIONADO: O que é recursão na programação e como ela é usada?

índice

  1. O que é recursão?
  2. Os perigos da recursão
  3. Use recursão com moderação

O que é recursão?

A resposta curta é que a recursão é basicamente toda vez que uma função chama a si mesma, geralmente com uma entrada diferente sendo passada para a função filha. Ele chama a si mesmo repetidamente até que uma condição de saída seja atendida e, em seguida, passa os resultados pela pilha de chamadas, potencialmente alterando-os também no caminho.

A resposta longa é que a recursão pode ajudar a resolver problemas complicados, dividindo-os em subconjuntos menores do problema principal. Muitas vezes, você terá estruturas de dados que contêm dados aninhados. Dividi-lo em pequenas quantidades de dados facilitará o processamento.

Por exemplo, suponha que cada folha da árvore tenha um valor associado e queremos encontrar a soma de todos os valores. Poderíamos fazer isso com uma função como a seguinte, que percorre a árvore folha por folha, inspecionando todos os filhos e calculando o total.

 int CountAllLeaves ( Leaf currentLeaf ) { // Começa com o valor atual int Total = currentLeaf . valor ; // Adiciona quaisquer filhos ao valor, se houver foreach ( Leaf childLeaf in currentLeaf . children ) { Total = Total + CountAllLeaves ( childLeaf ) ; } return Total; }

Funciona porque CountAllLeaves não se importa com qual planilha você a chama. É chamado repetidamente até atingir a última folha da árvore, que não tem filhos. Por esse motivo, ele simplesmente retorna seu próprio valor. Esse valor é passado para a planilha pai, que adiciona o valor do filho ao seu próprio valor e, em seguida, se repete para todos os irmãos que possuem a planilha filho. No final, o resultado final da função será a soma total de todas as folhas da árvore.

Em algum momento, você precisa chegar ao caso de prisão , que é a parte do problema cuja resposta é conhecida sem fazer mais chamadas recursivas. Caso contrário, a função se repetirá indefinidamente, causando o travamento do programa. Nesse caso, o caso de parada é quando a função atinge uma folha que não possui filhos.

Não precisa ser estruturas de dados aninhadas como árvores. Você pode escrever funções recursivas em torno de qualquer tipo de problema. Por exemplo, calcular o fatorial de um número envolve multiplicá-lo por qualquer número menor que ele. Você pode fazer isso muito facilmente com recursão:

 int Fatorial ( int n ) { if ( n <= 1 ) return 1 ; return n * Fatorial ( n - 1 ) ; }

Neste exemplo, o caso de parada é quando n atinge 1, onde eventualmente retorna um valor e a pilha de chamadas pode entrar em colapso.

Vamos dar uma olhada em um exemplo do mundo real. Neste trecho de código, há uma classe Container que contém vários elementos de interface do usuário anexados, bem como vários contêineres filho com seus próprios elementos. Ele deve ser "renderizado" em uma lista simples de itens que podem ser exibidos na tela.

Esta é basicamente outra estrutura de dados de árvore, então a abordagem é semelhante. Exceto que, neste caso, a variável total é uma lista, que obtém as listas de itens de cada contêiner adicionado a ela.

A mágica de fazer isso usando recursão é que ela preserva a ordem Z dos elementos. Elementos desenhados depois de outros elementos vêm à tona, então o menor container filho sempre aparecerá no topo. Nesse cenário, também achei útil ver os elementos de sobreposição, que são adicionados depois que os outros elementos e filhos terminam de renderizar.

Os perigos da recursão

Então, quando você deve usar recursão em seu código? A resposta é que você provavelmente deve evitá-lo na maioria das situações, especialmente quando uma solução iterativa usando um loop simples fará o trabalho.

Sempre que você chama uma função, seu programa aloca recursos para essa função. Todas as variáveis ​​e informações locais vão para a pilha, que é uma estrutura de dados LIFO (last-in, first-out). Isso significa que a última chamada de função é sempre a primeira a ser removida, como um bucket no qual o elemento superior é sempre removido.

O problema com a recursão é que você pode usar uma chamada de função aninhada para cada elemento processado. Isso resulta em uma sobrecarga muito maior, pois cada chamada de função requer seu próprio conjunto de variáveis ​​e parâmetros locais. Leve mais tempo de processamento do que uma abordagem baseada em loop.

Loops não tem esse problema. Após cada iteração do loop, o elemento superior da pilha será removido. Ele poderia processar um bilhão de itens usando a mesma pilha.

Usar muitos recursos com chamadas de função recursivas pode resultar em estouro de pilha, onde o programa só pode falhar com base em muitas chamadas aninhadas. Isso pode acontecer com conjuntos de dados particularmente grandes ou com algoritmos ruins, como recursão exponencial ou binária, que são chamados várias vezes por chamada de função.

Use recursão com moderação

A recursão é uma coisa boa para certos problemas, mas basicamente não há soluções recursivas para problemas que não podem ser resolvidos mesmo usando loops (exceto para recursão aninhada como a função de Ackerman). Mesmo estruturas de dados de árvore complicadas podem ser percorridas usando loops e pilhas. Se você precisar gerenciar grandes quantidades de dados ou se preocupar muito com o desempenho, talvez seja melhor usar uma solução iterativa.

O outro problema com a recursão é que ela pode levar a um código que é difícil para outras pessoas entenderem, já que geralmente dá um pouco de dor de cabeça para alguém descobrir. Embora muitas vezes pareça a solução mais "elegante", seu trabalho como programador não é se exibir, mas escrever código funcional e legível.

De qualquer forma, você vai querer pensar se o problema em questão seria melhor usar um loop. A recursão deve ser o último recurso para problemas que seriam muito mais complicados sem ela. Na verdade, em todas as minhas 40.000 linhas de código-fonte, eu só tinha um exemplo de recursão para este artigo.

E, depois de uma segunda olhada, notei um problema. Embora tenha funcionado bem, foi escrito de maneira preguiçosa e óbvia e, como tal, usa muito mais memória do que o necessário. Isso não é realmente um problema com as pequenas estruturas de dados que você está gerenciando, mas eu estava criando um new List<CuiElement>() para cada chamada de função e somando os resultados dos filhos abaixo. Isso significava que, se você recebesse um contêiner com filhos profundamente aninhados, ele arquivaria os mesmos dados repetidamente sem motivo.

A solução neste caso foi passar uma referência a uma lista externa para a função recursiva e adicionar todos os elementos a ela diretamente. Isso também envolveu a modificação da função Render() para uma função que manipulava a instalação de nível superior para a função de compilação recursiva.

Isso realiza exatamente a mesma solução de adicionar cada elemento na ordem correta, mas resolve o problema do uso de memória aumentando exponencialmente a cada chamada.

No entanto, estou satisfeito com esta função, pois é bastante concisa e permite que você faça o trabalho facilmente. Se eu fosse transformá-lo em uma solução usando um loop, seria muito mais complicado e faria exatamente a mesma coisa. Você deve pesar os prós e os contras de usar uma solução recursiva e usá-los apenas onde não espera um uso sério de recursos. Nesse caso, não espero chamar essa função com contêineres aninhados que contêm centenas de elementos, portanto, usar a recursão aqui é bom.


Para obter mais informações sobre este tópico, consulte nosso guia de recursão.

  • O que é "Black Crush" em uma tela?
  • Você pode colorir seus arquivos do Mac com rótulos

descubra mais conteúdo

Google Pixel Watch também tem detecção de queda, mas não até o próximo ano

As melhores xícaras de café aquecidas de 2022

Como automatizar seu iPhone com base na hora, atividade ou localização

"Isso deve ter sido antes do meu tempo"

Por que a NASA enviou Snoopy para a Lua? – Revisão geek

O que é uma carga fantasma?

Deixe uma resposta Cancelar resposta

A recursão é, em ciência da computação, uma maneira de atalho e resolver problemas. De fato, a recursão é uma das ideias centrais da ciência da computação.Resolver um problema por recursão significa que a solução depende das soluções de pequenas instâncias do mesmo problema.

O que é recursão em programação?

A recursão é um recurso das linguagens de programação que permite que um subprograma chame a si mesmo. A recursão é útil para resolver problemas definíveis em seus próprios termos. A recursão é, até certo ponto, análoga ao princípio da indução.

O que é recursão e exemplos?

A recursão consiste em funções chamando a si mesmas, evitando o uso de loops e outros iteradores. O futuro da programação é ��Machine Learning. Nosso mestre começa em 18 de outubro. Um exemplo fácil de ver e frequentemente utilizado é o cálculo do fatorial de um inteiro.

O que é recursão na programação Java?

A recursão na programação Java permite criar métodos e funções capazes de enumerar qualquer tipo de estrutura, por mais complexa que seja. Imagine que você tenha que criar uma função que percorra um sistema de arquivos inteiro, com pastas e subpastas.

O que é recursão na programação Python?

Funções recursivas são aquelas que, dentro de seu algoritmo, se referem a si mesmas.

Ir arriba