Estruturas de Dados Revisão de Funções e Recursão Prof. Ricardo J. G. B. Campello Agradecimentos Parte dos slides a seguir são adaptações dos originais em Pascal gentilmente cedidos pelo Prof. Rudinei Goularte 1
Sumário Funções em C Declaração, Escopo e Tipos de Passagem Funções Pré-Definidas em Bibliotecas Ativação de Funções Recursão Definição e Características Tipos de Recursão Funções em C Podemos ver uma função em C como uma rotina que recebe ou não valores como argumentos e retorna ou não explicitamente um valor Declaração: tipo nome (lista de parâmetros) { Exemplo: corpo da função (incluindo declarações de vars. locais) double quadrado (double x) { return x*x; 2
Funções em C Funções que não retornam valores são do tipo void void também é usado para indicar a inexistência de parâmetros Exemplo: void hello(void){ printf("hello World!"); O retorno de valor é feito através do comando return return interrompe imediatamente o fluxo de execução e retorna o valor especificado Funções em C Escopo: As variáveis declaradas dentro de uma função possuem escopo local, ou seja, provisório e interno à função Variáveis globais são declaradas no início de qualquer módulo (arquivo) de programa, fora de qualquer função incluindo main! Passagem de Parâmetros: Em geral, por valor (uma cópia do parâmetro é feita) Para forçar passagem por referência, é preciso utilizar ponteiros É o que ocorre necessariamente com arranjos (vetores/matrizes) 3
Funções Pré-Definidas As linguagens de programação têm à sua disposição várias funções pré-definidas Em C, essas funções são organizadas em bibliotecas Exemplo (Biblio. Matemática C ANSI #include <math.h>): pow(b,e) base b elevada ao expoente e sqrt(x) raiz quadrada de x Parâmetro formal Identificador (nome) Ativação de Funções Ativação e captura do resultado de uma função pode ser: Por atribuição direta do retorno a uma variável do mesmo tipo Por uso do retorno dentro de uma expressão Exemplo (H é real, A e Y são inteiros ou reais): H = sqrt(a + pow(y,2) + 2 * sin(y)); atribuição direta uso em expressão 4
Exemplo Dados dois números N e K, calcular a Combinação: C ( N, K ) N! = K!( N K)! Se existisse uma função fat(x) que calculasse o fatorial de um dado X, o cálculo acima ficaria: C[N,K] = fat(n) / (fat(k) * fat(n-k)) Se não existe, podemos defini-la função FAT(inteiro: X): retorna inteiro; início inteiro: P, I; P 1; para I de 1 até X faça P P * I; retorne P; fim Exemplo long int FAT(long int X){ long int I, P; P = 1; for(i=1; I<=X; I++) P=P*I; return P; 5
Exemplo #include <stdio.h> long int FAT(long int X); void main(void){ long int N, K, C; printf("entre com N:"); scanf("%d", &N); printf("entre com K:"); scanf("%d", &K); C = FAT(N)/(FAT(K)*FAT(N-K)); printf("\n C(N,K) = %d", C); /* continua... */ /* continua... */ long int FAT(long int X){ long int I, P; P = 1; for(i=1; I<=X; I++) P*=I; return P; Recursão Um procedimento ou função é dito recursivo se chama a si mesmo Muitas definições computacionais são (ou podem ser) recursivas. Exemplo: Sistemas Diretório-Arquivo consistem de um diretório cujo conteúdo consiste de arquivos e possivelmente outros diretórios Conceito está intimamente ligado com os conceitos matemáticos de relação de recorrência e indução a n = 2 a n-1, a 0 = 1 a n = 2 n 6
Recursão Em C, qualquer função pode ser usada recursivamente Exemplo: cálculo do fatorial long int FAT(long int x) { if (x == 1) return 1; else return x*fat(x 1); OU long int FAT(long int x) { return (x == 1? 1 : x*fat(x 1) ); /* EPC: Modifique para funcionar para x = 0 */ Recursão Exige condição de parada e mudança de estado ou entra em ciclo infinito até estourar a pilha de recursão Quais são elas no código recursivo do fatorial??? Vantagens Muitos problemas são intrinsecamente recursivos Código possivelmente mais claro (simples) Desvantagens Velocidade (chamadas a módulos demandam tempo) Uso adicional de memória (pilha de recursão) Análise teórica possivelmente mais complexa 7
Recursão float vetsum(float v[], int n){ if (n == 0) return v[0]; else return ( v[n] + vetsum(v, n 1) ); retorna 15 + v [4] = 15 + 5 = 20 vetsum(v,4) retorna 13 + v [3] = 13 + 2 = 15 vetsum(v,3) Traço de recursão: Exemplo com v = [4 3 6 2 5] vetsum(v,2) vetsum(v,1) vetsum(v,0) retorna 7 + v [2] = 7 + 6 = 13 retorna 4 + v [1] = 4 + 3 = 7 retorna v [0] = 4 Recursão Alguns Tipos de Recursão: Recursão Linear: Módulo com apenas uma chamada recursiva Recursão Binária: Módulo com duas chamadas recursivas Recursão Múltipla: Módulo com múltiplas chamadas recursivas Recursão Indireta: Módulo A chama B que chama A Recursão de Cauda: Recursão linear cuja chamada recursiva é a última operação do módulo Note que, tecnicamente, a recursão da função fat vista anteriormente não é de cauda, mesmo estando na última declaração do módulo: de fato, a última operação do módulo é uma multiplicação no entanto, é usual chamar esse tipo de recursão como de cauda 8
Recursão Sempre existe versão iterativa de um programa recursivo Conversão nem sempre é trivial Normalmente simples para recursão do tipo cauda Exemplo 1: impressão de vetor void printvet(float v[], int i, int f){ if (i<=f){ printf("%f ", v[i]); printvet(v,i+1,f); /* recursão de cauda */ Recursivo Iterativo void printvet(float v[], int i, int f){ int j; for (j=i; j<=f; j++) printf("%f ", v[j]); Recursão Exemplo 2: inversão de vetor void invvet(float v[], int i, int f){ float aux; if (i<f){ aux = v[i]; v[i] = v[f]; v[f] = aux; invvet(v,i+1,f 1); Exercício: A recursão acima é de cauda? Como é a versão iterativa dessa mesma função? 9
Recursão Situações onde se recomenda usar recursão: Quando o problema é intrinsecamente recursivo (e.g. Série de Fibonacci) e sua solução como tal traz maior clareza e não implica ineficiência em termos de tempo de execução e uso de memória. Situações onde se deve evitar recursão: Quando existe um algoritmo iterativo igualmente claro e simples P. ex. quando a recursão é de cauda P. ex. quando uma única recursão já leva à condição de parada Quando a recursão é ineficiente perante a uma versão iterativa P. ex. quando muitos parâmetros são passados por valor P. ex. quando pode haver sobrecarga da pilha de recursão Exercícios Qual o efeito final de inverter as linhas 3 e 4 do procedimento recursivo printvet visto anteriormente? Escreva uma função recursiva que receba uma base real e um expoente inteiro e retorne o valor da base elevada ao expoente. É simples provar por indução matemática que a relação de recorrência T n = 2T n 1 + 1 é, para T 0 = 0, equivalente a T n = 2 n 1. Escreva duas funções que recebam um inteiro n e retornem T n, sendo uma recursiva e a outra não. Implemente uma função para calcular o n-ésimo elemento da seqüência de Fibinacci de forma recursiva. 10
Bibliografia Schildt, H. "C Completo e Total", 3a. Edição, Pearson, 1997. Damas, L. Linguagem C, 10a. Edição, LTC, 2007 11