Article image
Gustavo Pereira
Gustavo Pereira17/04/2024 20:28
Compartilhe

Navegando pela lógica da Recursão: Como funciona e por que ela é tão relevante

  • #Lógica de Programação
  • #Boas práticas
  • #Java

Introdução

Minha Experiência com Recursão

Iniciando o artigo e também demonstrado o meu objetivo com esse artigo, minha experiência com recursividade foi muito turbulenta. Eu cheguei até a conhecer o escopo de uma função recursiva há cerca de um ano, mas só comecei a aprender e utilizá-la mesmo esse ano para cá pela minha Faculdade, devido ao meu professor de Lógica de Programação e pela explicação ótima dele. Contudo, infelizmente vejo que existem algumas pessoas que ou não tiveram a mesma sorte que eu ou não tem conhecimento da utilidade de “chamar uma função dentro da mesma”, mesmo que seja muito essencial em entrevistas de empregos ou testes envolvendo a lógica de programação. Assim, irei te dar um norte para que você saia daqui ao menos tendo uma noção básica dela e já sabendo o que fazer depois de receber esse conhecimento.

A utilização da recursividade em coisas que utilizamos

Vamos para um exemplo muito pertinente, mas que também é interessante para se introduzir nessa classificação de função: Imagino que vocês, neste exato momento, estejam vendo esse artigo por um navegador (especificamente em um computador). Nele, ao lado da barra de pesquisa principal, existem três ícones que, com certeza, muitos de vocês costumam utilizar, nem mesmo que seja com atalhos: A opção de reiniciar, de voltar e avançar a página – que podem ou não aparecer para você dependendo de certas condições.

Contudo, focaremos nas “setinhas”: ao iniciar uma nova guia, a opção de voltar estará bloqueado e a de avançar simplesmente não aparecerá, o que acaba mudando, quando você, por exemplo, acessa uma página qualquer e se aprofunda nela, e, após tudo, isso, retorna uma página anterior. Além disso, existe o explorador de arquivos, que contém ambas as duas setas para você ir mudando de diretório para diretório sem precisar escolher “na mão” os mesmos. Ambos podem até ter propostas e formas diferentes para o desenvolvimento das funcionalidades, mas utilizam da mesma lógica-base da recursividade.

Desenvolvimento

O Paradigma da Modularização e sua conexão com recursividade

Antes de tudo, vamos olhar ou rever o conceito da modularização: Ela envolve a divisão de um sistema em partes independentes e interconectadas, chamadas de módulos. Dentre esses módulos esses 2 tipos específicos; as procedures (procedimentos ou void) e funções ou métodos.

A Procedure, na maior parte das vezes coleta variáveis externas (globais) ou internas (parâmetros e locais) para ser desenvolvidos instruções determinadas com as variáveis separadas para isso que normalmente envolvem no final o output (impressão) de algo e ou alteração de algum valor externo, seja uma variável ou também em um arquivo, banco de dados ou API, por exemplo.

No caso do Método, ela contém as mesmas possibilidades de uma procedure com a obrigação dela retornar um tipo de dado específico (que normalmente é uma forma mais segura e melhor de se passar valores de um módulo a uma variável externa dele ao invés de forçá-la a ser global e, por sua vez, causar possíveis problemas devido a alterações indesejadas dessas variáveis no próprio código). Acontece, que, além de você poder retornar um valor, você pode retornar também o resultado de uma outra função e até da própria função, configurando para esse último caso uma condição (o caso base) para retornar um valor quando ela se cumprir e evitar a chamada infinita do método

O conceito da Recursividade e o que é uma função recursiva

Relembrando isso, voltemos ao exemplo das setas do navegador com algumas das funcionalidades dela:

  • Cada vez que você clica na seta “Voltar”, o navegador realiza uma chamada à função interna que gerencia o histórico (que pode chamar a si mesma para continuar navegando pelo histórico)
  • Quando você clica em uma das setas ele exibe a página anterior (ou a próxima) na sequência de páginas visitadas na sua guia recente
  • Referente ao Caso Base, ele ocorre quando você atinge o início ou o fim do histórico (ou seja, não há mais páginas anteriores ou posteriores).

Por esses fatores é possível ver algumas propriedades, como a auto chamada (a função chamando a si mesma), a Progressão para o caso base (que consiste em incrementar ou decrementar o parâmetro de auto chamada da função até que ele seja semelhante ao que o caso base pede e não ocorra loop infinito) e o próprio caso base. Ambas as três compõem exatamente o que uma função recursiva é, comprovando que essa funcionalidade e outras como a de Explorador de Arquivos (que são até mais complexas que essa devido ao fator hierárquico das pastas) são baseadas nesse mecanismo da programação.

Entendendo na prática como funciona

Para ficar de mais fácil compreensão, pense no desenvolvimento de um algoritmo que calcula a potência de um certo número, coletando o seu Expoente e a Base. Primeiramente precisamos do input do valor da base e do expoente e uma variável para armazenar o resultado. Após isso teremos que utilizar um for com um índice que limita a multiplicação do número base para ele mesmo pelo seu valor do expoente (a quantidade de vezes que o número irá multiplicar ele mesmo) e, assim, o código está feito. Agora, fica uma dúvida: é possível converter esse código e outros que utilizam o for ou outras estruturas de laços/loops para uma função recursiva? A resposta é sim e veremos abaixo como seria isso em 6 linguagens de programação conhecidas por mim e imagino que seja, ao menos algumas dessas, para vocês também:

image

Mesmo sendo de linguagens diferente é possível ver algumas semelhanças, como as regras que eu cheguei a citar anteriormente: o caso base (if) com o retorno de um valor padrão, a auto chamada da função e a progressão dela para o caso base (que é decrementando o expoente ou 2º parâmetro)

Dessa forma, suponha, nesse formato de código independente da linguagem que o usuário deseje calcular a seguinte potência: 2^4 (dois elevado à quatro):

image

Como visto na imagem acima, a base e o expoente nos parâmetros para a chamada externa e inicial da função será de respectivamente 2 e 4. Sempre for processado a função devido a uma chamada, será verificado o caso de base (que nessa questão inicial dará falso, pois 4 não é igual a 0). Sendo falsa a condição será então retornado a auto chamada da função, com o decremento do expoente - que está diretamente relacionado ao caso de base -, que será feito essa repetição até que o valor do expoente seja zero, que por sua vez, retornará “1” ao ocorrer isso. Finalmente após o caso base ser verdadeiro, a “pilha” das auto chamadas da função que chegou a se formar serão possíveis de ser calculadas, isto é, da última chamada feita até a primeira (ou seja, com o retorno de 1, agora a chamada “Recursiva(2,0)”, que retorna esse valor, retornará esse valor para o cálculo do resultado da “Recursiva(2,1)”, sendo possível agora calcular essa chamada, que calculará o valor 2 da base vezes o resultado anterior 1. Com isso feito, ela retornará o valor para a “Recursiva(2,2)” e assim por diante, sempre implementando essa mesma lógica até que a pilha se complete e finalize a chamada principal da função) retornando assim o valor final 16 para a chamada inicial externa.

Como visto, a utilização de loops quanto a de recursividade para os cálculos tem seus prós e contras (principalmente pela escalabilidade e complexidade de cada uma) assim cabe então verificar a situação que foi imposta e qual dos dois seriam melhores para a resolução.

Conclusão

Finalizando esse Artigo, convido a todos também a desenvolver algoritmos que, com base em outros utilizem uma única estrutura de laço para os cálculos, tentem convertê-los para funções recursivas. Pode parecer difícil (e é um conceito realmente complicado de entender de primeira), então tente ler e reler esse artigo até que consiga ao menos entender o funcionamento. mas caso ainda tenha dúvida, utilize o exemplo didático do funcionamento do cálculo acima que desenvolvi em seus desenvolvimentos de códigos referentes a isso e é utilizado para verificar se o código está certo e evitar problemas referente a estouro de pilha (Stack Overflow). Sobre exemplos para treinar recursividade recomendo outros cálculos matemáticos como a Sequência de Fibonacci, Séries de cálculos e derivados

Além disso, pesquise mais sobre a Recursividade em si e as vantagens e desvantagens em relação as estruturas de repetição. Isso também muito relevante para ter um senso crítico melhor de quando e onde ela pode ser utilizada em também um âmbito da empresa por exemplo

Enfim, espero que tenham gostado desse meu primeiro Artigo na plataforma da DIO, e que esse seja o primeiro de muitos!

Compartilhe
Comentários (0)