Linguagem de
programação I A
Carlos Oberdan Rolim
Ciência da Computação
Sistemas de Informação
Recursão
Recursão
Recursão é o ato de uma função chamar ela mesma. Uma
função que chama a ela mesma é chamada função
recursiva.
O exemplo padrão de função recursiva é uma função que
calcula o fatorial de um número. O fatorial de um número é
igual ao produto dos números inteiros de 1 até o número.
fatorial de 3 = 3 * 2 * 1
Se você observar com cuidado verá que:
fatorial de 3 = 3 * fatorial de 2
fatorial de 2 = 2 * fatorial de 1
fatorial de 1 = 1 * fatorial de 0
Ou seja
fatorial de um número = número * (fatorial de número - 1)
Fatorial recursivo
Definição não recursiva (tradicional):
N! = 1, para N = 0.
N! = 1 x 2 x 3 x .... x N, para N>0
Definição recursiva:
N! = 1, para N = 0;
N! = N x (N - 1)!, para N > 0.
Decomposição do Fatorial(3)
Fatorial(3) = 3 * Fatorial(2)
Fatorial(2) = 2 * Fatorial(1)
Fatorial(3) = 3 * 2 = 6
Fatorial(2) = 2 * 1 = 2
Fatorial(1) = 1*Fatorial(0)
Fatorial(1) = 1*1 = 1
Fatorial(0)=1
Características dos programas recursivos
a) Chama a si mesmo para resolver parte do
problema;
chamada recursiva
b) Existe pelo menos um caso base que é
resolvido sem chamar a si mesmo.
Condição de parada
Projeto para implementar funções recursivas
Toda função recursiva possui dois elementos:
•
Resolver parte do problema (caso base) ou
•
Reduzir o tamanho do problema (caso geral).
No caso do nosso exemplo, Fatorial(0) é o
caso base
Recursão
/* Exemplo de função recursiva */
#include <stdio.h>
int fatorial(int nr) {
if(nr == 1) return(1);
return (nr * fatorial(nr-1) );
}
int main() {
int a;
printf("\nEntre com um valor inteiro :");
scanf("%d",&a);
printf("O fatorial de %d é %d\n\n",a, fatorial(a));
return(0);
}
Segmentos de memória
Stack (pilha): local onde as variáveis
locais as funções são armazenadas
Armazena os dados de chamadas de
funções
Heap
p = (int *) malloc...
Objetos
Dados são “empilhados” no topo da pilha
Usa FIFO (First In First Out)
Heap: local para os dados alocados
dinamicamente, variáveis globais,
constantes e definidas como “static”
Stack
int main()
Int y;
Valores são armazenados de forma
“desordenada”
Tempo de execução
Segmento de código: local onde os
dados de código compilados residem
Codigo
Chamada de método
Quando um método é chamado:
é necessários inicializar os parâmetros formais com os valores
passados como argumento;
sistema precisa saber onde reiniciar a execução do programa;
Informações de cada método (variáveis e endereço de retorno)
deve ser guardada até o método acabar a sua execução.
Mas como o programa diferencia a variável n da primeira
chamada da variável n da segunda chamada do método
fatorialr?
int fatorial(int nr) {
if(nr == 1)
return(1);
return (nr * fatorial(nr-1) );
}
Registro de ativação
Registro de ativação:
área de memória que guarda o estado de uma função, ou
seja:
variáveis locais
valores dos parâmetros;
endereço de retorno (instrução após a chamada do método corrente);
valor de retorno.
Registro de ativação são criados na pilha em tempo de
execução;
Existe um registro de ativação (um nó na pilha) para cada
método;
Quando um método é chamado é criado um registro de
ativação para este e este é empilhado na pilha;
Quando o método finaliza sua execução o registro de ativação
desse método é desalocado.
Registro de ativação
Registro de
ativação de
f3()
Registro de
ativação de
f2()
Registro de
ativação de
f1()
Registro de
ativação do
método
main()
Parâmetros e
variáveis locais
Endereço de retorno
Valor de retorno
Parâmetros e
variáveis locais
Endereço de retorno
Valor de retorno
Parâmetros e
variáveis locais
Endereço de retorno
Valor de retorno
topo
void f3() {
int x = 3;
}
void f2() {
int x = 2;
f1();
}
void f1() {
int x = 1;
f2();
}
int main() {
f1();
}
n=1
Registro de
ativação de
fat(1)
1
Registro de
ativação de
fat(4)
=24
3*fat(2) =6
4*fat(3)
topo
PC=6
2*fat(1)
2
n=3
Registro de
ativação de
fat(3)
fat (4)  main
PC=6
n=2
Registro de
ativação de
fat(2)
fatorial de 4
topo
topo 3 int fatorial(int n) {
PC=6
6
n=4
PC = 11
24
resultado =24
…
topo
=2
1
4
5
if(n == 1)
return(1);
6
return (n * fatorial(n-1) );
7
8
}
9 int main(){
10
int resultado
11
resultado = fatorial(4);
12
printf(”%d”, resultado);
13 }
Registro de ativação
A cada término de FATORIAL, o controle retorna para a
expressão onde foi feita a chamada na execução
anterior, e o último conjunto de variáveis que foi alocado
é liberado nesse momento. Esse mecanismo utiliza uma
pilha.
A cada nova chamada do método FATORIAL, um novo
conjunto de variáveis (n, FATORIAL) é alocado.
Vantagens e Desvantagens
Vantagens da recursão
Redução do tamanho do código fonte
Maior clareza do algoritmo para problemas de definição
naturalmente recursiva
Desvantagens da recursão
Baixo desempenho na execução devido ao tempo para
gerenciamento das chamadas
Dificuldade de depuração dos subprogramas recursivos,
principalmente se a recursão for muito profunda
Evitando recursão
A recursão sempre deve ser evitada basicamente por
dois fatores.
Primeiro que uma função recursiva é difícil de compreender.
Segundo que as funções recursivas são mais lentas que suas
correspondentes não recursivas.
Normalmente uma função recursiva também pode ser
escrita com laços de repetição tipo for ou while de modo
a remover a recursão.
Download

Recursão