Recursividade em ABAP

Uma das técnicas de programação mais incompreendida e evitada, também é umas das mais poderosas, a Recursividade. Eu precisei usar a recursividade apenas uma única vez na vida com ABAPer e o resultado foi muito interessante.

Nesse texto eu vou resolver um problema muito simples usando recursividade, exponenciação.

Exponenciação

De acordo com  a Wikipédia:

Exponenciação ou potenciação é uma operação matemática, escrita como An, envolvendo dois números: a base a e o expoente n. Quando n é um número natural maior do que 1, a potência an indica a multiplicação da base a por ela mesma tantas vezes quanto indicar o expoente n.

A fórmula da exponenciação é simples e por isso considero um exemplo legal para mostrar a recursividade em prática.

Exponenciação Usando Recursividade

No ABAP você pode usar o operador ** para fazer a exponenciação e resolver todo o problema em uma linha (veja no programa abaixo), mas esse post não é sobre exponenciação, mas recursividade.

Novamente da Wikipédia:

 A recursão é o processo pelo qual passa um certo procedimento quando um dos passos do procedimento em questão envolve a repetição completa deste mesmo procedimento. Um procedimento que se utiliza da recursão é dito recursivo. Também é dito recursivo qualquer objeto que seja resultado de um procedimento recursivo.

Em ABAP claro significa uma subrotina (form, módulo de função ou método) que efetua uma chamada dela mesmo.

Por exemplo, usando a recursão para resolver um problema de potência, farei com que o método EXECUTE_EXPONENTIATION( ) seja chamado inúmeras vezes por ele mesmo, de acordo com o expoente passado para ele como parâmetro.

Também poderíamos usar a estrutura de loop DO n TIMES … ENDDO, mas aí não seria recursão.

Veja o programa abaixo:

ZABAP101_RECURSION

Execute esse programa no modo debug e veja a pilha de chamada. Veja que o método se repete várias vezes indicando que ele fora chamado de dentro dele mesmo, caracterizando uma recursividade:

Pilha de chamadas do método EXECUTE_EXPONENTIATION( ) – Recursividade

A primeira linha do método incrementa o atributo INTERECTION, daí o método chama ele mesmo se a interação for menor ou igual ao expoente. Com isso a pilha (stack) vai sendo adicionada a cada nova chamada até que o número máximo de iterações (expoente) seja alcançado.

A pilha pára de ser incrementada e o restante do método começa a ser chamado, a partir do item da pilha mais novo (LIFO, Last In First Out), onde finalmente a multiplicação é feita e sendo acumulada no atributo POWER.

Explosão de Lista Técnica

Existem problemas em que a recursividade é quase que obrigatória, no caso da chamada explosão de lista técnica.

Imagine que você precise saber o custo de algum material que é composto por outros materiais, como por exemplo um notebook.

Partes de notebook – fonte imagem

Um notebook pode ser um materia formado por teclado, display LCD, HD, placa-mãe, memórias, carcaça etc.

O teclado é formado por uma base, uma tecla Q, outra W, outra E, outra R, outra T etc.

Um HD é formado por um conjundo de discos magnéticos, placa de controle e sistema de leitura,

Um sistema de leitura é formado por um braço de leitura, cabeçote de leitura e placa de leitura.

E assim por diante.

Se eu tivesse uma rotina que dado um material ele me retornasse uma lista de materais que formam esse material, eu precisaria chamar essa rotina n vezes, conforme eu fosse “desmontando” ou “explodindo” meu material.

Dessa maneira a haveria a recursividade como exemplificado no exemplo do notebook, resolvendo o problema muito rapidamente.

Claro que nem tudo são flores. Depurar um programa que usa recursividade é muito trabalhoso, bem como entender a recursividade que outra pessoa desenvolveu. Isso faz com que a recursão seja evitada, mas em alguns casos é a melhor forma de resolver um problema.

6 Resultados

  1. Antelio I. Abe disse:

    Furlan,

    Outro local interessante para uso efetivo de recursividade é na hierarquia de Elemento PEP junto ao cálculo de custos baseados na seleção de entradas na COVP ou COBK/COEP.

    Quantas vezes vi programas hard-coded com o número de “profundidade” de hierarquia.

    Funciona, mas fica feio e para começar a dar problema ninguém vai perceber.

    Abs.

  2. Fawcs disse:

    Isso me lembra equações diferenciais ordinárias lineares resolvidos por série de potências!

    anyways, isso gera uma pilha de chamada somente ou gera objetos temporarios novos? pq se nao, comeria uma certa memória, correto? Claro que é só um exemplo, mas, dúvida é dúvida=P

    • Flávio Furlan disse:

      Olá Adriano,
      Não sei bem ao certo o que você disse sobre as equações diferenciais ordinárias, mas acho que é algum assunto bem legal 🙂

      O objeto gerado é o mesmo, somente as variáveis locais que são replicadas. Se eu tivesse criado o objeto dentro do método recursivo aí sim teríamos múltiplas cópias.

  3. Guilherme Fujii disse:

    Isso é muito usado para buscar a hierarquia de cargos ou unidades organizacionais…

    Voce acha que seria loucura verificar se o banco de dados é Oracle e se sim fazer um EXEC SQL para executar um select nativo do Oracle? Considerando que o Oracle possui uma clausula especial para esse tipo de select e provavelmente a performance será melhor?

    Abraços!

    • Flávio Furlan disse:

      Guilherme,
      Excelente observação! O problema de usar SQL nativo é que nem todos desenvolvedores conhecem e para dar manutenção fica bem custoso.

      Você já fez alguma experiência usando open SQL no ABAP e Oracle Nativo e comparando resultados? Numa dessa descobrimos que nem é tanta diferença ou o ganho de performance seja tão alto que valha a pena as perdas na hora de dar manutenção.

  4. Guilherme Fujii disse:

    Sim, penso por esse lado também.. Eu lembrei desse assunto com o seu post, vou desenvolver uma lógica que use um select recursivo com as 2 opções e fazer um teste… Preciso lembrar também de um servidor que utiliza Oracle rs

    Maas pensando bem, com o SQL nativo se não me engano vou precisar buscar linha a linha e utilizar um EXEC SQL PERFORMING… ou um FETCH NEXT, ele deve conectar várias vezes com o banco de dados comprometendo a performance, assim como o SELECT e ENDSELECT…

    Acho que só testando pra saber…