Você está na página 1de 39

Programao Funcional

Captulo 5

Funes Recursivas
Jos Romildo Malaquias
Departamento de Computao Universidade Federal de Ouro Preto

2013.1
1/39

Funes recursivas

Recursividade mtua

Recursividade de cauda

2/39

Tpicos

Funes recursivas

Recursividade mtua

Recursividade de cauda

3/39

Recursividade

Recursividade uma idia inteligente que desempenha um papel central na programao funcional e na cincia da computao em geral. Recursividade o mecanismo de programao no qual uma denio de funo ou de outro objeto refere-se ao prprio objeto sendo denido. Assim funo recursiva uma funo que denida em termos de si mesma. Recursividade o mecanismo bsico para repeties nas linguagens funcionais. So sinnimos: recursividade, recurso, recorrncia.

4/39

Estratgia recursiva

Estratgia para a denio recursiva de uma funo:


1. dividir o problema em problemas menores do mesmo tipo 2. resolver os problemas menores (dividindo-os em problemas ainda menores, se necessrio) 3. combinar as solues dos problemas menores para formar a soluo nal

Ao dividir o problema sucessivamente em problemas menores eventualmente os casos simples so alcanados:


no podem ser mais divididos suas solues so denidas explicitamente

5/39

Denio recursiva

De modo geral, uma denio de funo recursiva dividida em duas partes: H um ou mais casos base que dizem o que fazer em situaes simples, onde no necessria nenhuma recurso.
Nestes casos a resposta pode ser dada de imediato, sem chamar recursivamente a funo sendo denida. Isso garante que a recurso eventualmente pode parar.

H um ou mais casos recursivos que so mais gerais, e denem a funo em termos de uma chamada mais simples a si mesma.

6/39

Exemplo: fatorial

A funo que calcula o fatorial de um nmero natural pode ser denida recursivamente como segue:
fatorial :: Integer -> Integer fatorial n | n == 0 = 1 | n > 0 = fatorial (n-1) * n

A primeira equao estabelece que o fatorial de 0 1. Este o caso base. A segunda equao estabelece que o fatorial de um nmero positivo o produto deste nmero e do fatorial do seu antecessor. Este o caso recursivo. Observe que no caso recursivo o subproblema fatorial (n-1) mais simples que o problema original fatorial n e est mais prximo do caso base fatorial 0 .

7/39

Exemplo: fatorial (cont.)

Aplicando a funo fatorial:


fatorial 6 fatorial 5 * 6 (fatorial 4 * 5) * 6 ((fatorial 3 * 4) * 5) * 6 (((fatorial 2 * 3) * 4) * 5) * 6 ((((fatorial 1 * 2) * 3) * 4) * 5) * 6 (((((fatorial 0 * 1) * 2) * 3) * 4) * 5) * 6 (((((1 * 1) * 2) * 3) * 4) * 5) * 6 ((((1 * 2) * 3) * 4) * 5) * 6 (((2 * 3) * 4) * 5) * 6 ((6 * 4) * 5) * 6 (24 * 5) * 6 120 * 6 720

8/39

Exemplo: fatorial (cont.)

Exerccio 1 Digite a funo fatorial em um arquivo fonte Haskell e carregue-o no ambiente interativo de Haskell. a) Mostre que fatorial 7 = 5040. b) Determine o valor da expresso fatorial 7 usando o ambiente interativo. c) Determine o valor da expresso fatorial 1000 usando o ambiente interativo. Se voc tiver uma calculadora cientca, verique o resultado na calculadora. d) Qual o valor esperado para a expresso
div (fatorial 1000) (fatorial 999)? Determine o valor desta expresso

usando o ambiente interativo. e) O que acontece ao se calcular o valor da expresso fatorial (-2)?

9/39

Exemplo: potncias de 2

A funo que calcula a potncia de 2 (isto , a base 2) para nmeros naturais pode ser denida recursivamente como segue:
potDois :: Integer -> Integer potDois n | n == 0 = 1 | n > 0 = 2 * potDois (n-1)

A primeira equao estabelece que 20 = 1. Este o caso base. A segunda equao estabelece que 2n = 2 2n1 , sendo n > 0. Este o caso recursivo. Observe que no caso recursivo o subproblema potDois (n-1) mais simples que o problema original potDois n e est mais prximo do caso base
potDois 0 .

10/39

Exemplo: potncias de 2 (cont.)

Aplicando a funo potncia de 2:


potDois 4 2 * potDois 3 2 * (2 * potDois 2) 2 * (2 * (2 * potDois 1)) 2 * (2 * (2 * (2 * potDois 0))) 2 * (2 * (2 * (2 * 1))) 2 * (2 * (2 * 2)) 2 * (2 * 4) 2 * 8 16

11/39

Exemplo: potncias de 2 (cont.)

Exerccio 2 Considere a seguinte denio para a funo potncia de 2:


potDois :: Integer -> Integer potDois n | n == 0 = 1 | otherwise = 2 * potDois (n-1)

O que acontece ao calcular o valor da expresso potDois (-5)?

12/39

Exemplo: multiplicao

A multiplicao de inteiros est disponvel na biblioteca como uma operao primitiva por questes de ecincia. Porm ela pode ser denida usando recursividade em um de seus argumentos:
mul mul | | | :: Int -> m n n == 0 n > 0 otherwise Int -> Int = 0 = m + mul m (n-1) = negate (mul m (negate n))

A primeira equao estabelece que quando o multiplicador zero, o produto tambm zero. Este o caso base. A segunda equao estabelece que m n = m + m (n 1), sendo n > 0. Este um caso recursivo. A terceira equao estabelece que m n = (m (n)), sendo n < 0. Este outro caso recursivo.

13/39

Exemplo: multiplicao (cont.)

Aplicando a funo multiplicao:


mul 7 (-3) negate (mul negate (mul negate (7 + negate (7 + negate (7 + negate (7 + negate (7 + negate (7 + negate 21 -21 7 (negate (-3))) 7 3) mul 7 2) (7 + mul 7 1)) (7 + (7 + mul 7 0))) (7 + (7 + 0))) (7 + 7)) 14)

A denio recursiva da multiplicao formalisa a idia de que a multiplicao pode ser reduzida a adies repetidas.

14/39

Exemplo: multiplicao (cont.)

Exerccio 3 Mostre que mul 5 6 = 30.

15/39

Exemplo: sequncia de Fibonacci

Na seqncia de Fibonacci 0, 1, 1, 2, 3, 5, 8, 13, . . . os dois primeiros elementos so 0 e 1, e cada elemento subseqente dado pela soma dos dois elementos que o precedem na seqncia. A funo a seguir calcula o n-simo nmero de Fibonnaci, para n 0:
fib fib | | | :: Int n n == 0 n == 1 n > 1 -> Int = 0 = 1 = fib (n-2) + fib (n-1)

A primeira e segunda equaes so os casos base. A terceira equao o caso recursivo.

16/39

Exemplo: sequncia de Fibonacci (cont.)

Neste caso temos recurso mltipla, pois a funo sendo denida usada mais de uma vez em sua prpria denio. Aplicando a funo de bonacci:
fib 5 fib 3 + fib 4 (fib 1 + fib 2) + (fib 2 + fib 3) (1 + (fib 0 + fib 1)) + ((fib 0 + fib 1) + (fib 1 + fib 2)) (1 + (0 + 1)) + ((0 + 1) + (1 + (fib 0 + fib 1))) (1 + 1) + (1 + (1 + (0 + 1))) 2 + (1 + (1 + 1)) 2 + (1 + 2) 2 + 3 5

17/39

Exemplo: sequncia de Fibonacci (cont.)

Exerccio 4 Mostre que fib 6 = 8.

18/39

Tpicos

Funes recursivas

Recursividade mtua

Recursividade de cauda

19/39

Recursividade mtua

Recursividade mtua ocorre quando duas ou mais funes so denidas em termos uma da outra.

20/39

Exemplo: par e mpar

As funes da biblioteca even (par) e odd (mpar), que determinam se um nmero par ou mpar, respectivamente, geralmente so denidas usando o resto da diviso por dois:
par, impar :: Int -> Bool par n = mod n 2 == 0 impar n = not (par n)

21/39

Exemplo: par e mpar (cont.)


No entanto elas tambm podem ser denidas usando recursividade mtua:
par :: Int -> Bool par n | n == 0 = True | n > 0 = impar (n-1) | otherwise = par (-n) impar :: Int -> Bool impar n | n == 0 = False | n > 0 = par (n-1) | otherwise = impar (-n)

Zero par, mas no mpar. Um nmero positivo par se seu antecessor mpar. Um nmero positivo mpar se seu antecessor par. Um nmero negativo par (ou mpar) se o seu oposto for par (ou mpar).
22/39

Exemplo: par e mpar (cont.)

Aplicando as funo par e impar:


par (-5) par 5 impar 4 par 3 impar 2 par 1 impar 0 False

23/39

Tpicos

Funes recursivas

Recursividade mtua

Recursividade de cauda

24/39

Recursividade de cauda

Uma funo recursiva apresenta recursividade de cauda se o resultado nal da chamada recursiva o resultado nal da prpria funo. Se o resultado da chamada recursiva deve ser processado de alguma maneira para produzir o resultado nal, ento a funo no apresenta recursividade de cauda.

25/39

Recursividade de cauda (cont.)

Exemplo: A funo recursiva a seguir no apresenta recursividade de cauda:


fatorial :: Integer -> Integer fatorial n | n == 0 = 1 | n > 0 = fatorial (n-1) * n

No caso recursivo, o resultado da chamada recursiva fatorial (n-1) multiplicado por n para produzir o resultado nal.

26/39

Recursividade de cauda (cont.)

Exemplo: A funo recursiva a seguir no apresenta recursividade de cauda:


par par | | :: Integer -> Bool n n == 0 = True n > 0 = not (par (n-1))

No caso recursivo, a funo not aplicada ao resultado da chamada recursiva par (n-1) para produzir o resultado nal.

27/39

Recursividade de cauda (cont.)

Exemplo: A funo recursiva potenciaDois a seguir apresenta recursividade de cauda:


potenciaDois :: Integer -> Integer potenciaDois n = potenciaDois n 1 potenciaDois :: Integer -> Integer -> Integer potenciaDois n y | n == 0 = y | n > 0 = potenciaDois (n-1) (2*y)

No caso recursivo, o resultado da chamada recursiva potenciaDois (n-1) (2*y) o resultado nal.

28/39

Recursividade de cauda (cont.)

Exerccio 5 Mostre que potenciaDois 5 = 32. Exerccio 6 Faa uma denio recursiva da funo par usando recursividade de cauda.

29/39

Otimizao de chamada de cauda

Em muitas implementaes de linguagens de programao uma chamada de funo usa um espao de memria (quadro, frame ou registro de ativao) em uma rea da memria (pilha ou stack) onde so armazenadas informaes importantes, como:
argumentos da funo variveis locais variveis temporrias endereo de retorno da funo

30/39

Otimizao de chamada de cauda (cont.)


Uma chamada de cauda acontece quando uma funo chama outra funo como sua ltima ao, no tendo mais nada a fazer. O resultado nal da funo dado pelo resultado da chamada de cauda. Em tais situaes o programa no precisa voltar para a funo que chama quando a funo chamada termina. Portanto, aps a chamada de cauda, o programa no precisa manter qualquer informao sobre a funo chamadora na pilha. Algumas implementaes de linguagem tiram proveito desse fato e na verdade no utilizam qualquer espao extra de pilha quando fazem uma chamada de cauda. Esta tcnica chamada de eliminao da cauda, otimizao de chamada de cauda ou ainda otimizao de chamada recursiva. A otimizao de chamada de cauda permite que funes com recursividade de cauda recorram indenidamente sem estourar a pilha. Muitas linguagens funcionais no possuem estruturas de repetio e usam funes recursivas para fazer repeties. Nestes casos a otimizao de chamada de cauda fundamental para uma boa ecincia dos programas.

31/39

Vantagens de usar recursividade

Muitas funes podem ser naturalmente denidas em termos de si mesmas. Propriedades de funes denidas usando recurso podem ser provadas usando induo, uma tcnica matemtica simples, mas poderosa.

32/39

Exerccios

Exerccio 7 O fatorial duplo de um nmero natural n o produto de todos os nmeros de 1 (ou 2) at n, contados de 2 em 2. Por exemplo, o fatorial duplo de 8 8 6 4 2 = 384, e o fatorial duplo de 7 7 5 3 1 = 105. Dena uma funo para calcular o fatorial duplo usando recursividade. Exerccio 8 Dena uma funo recursiva que recebe dois nmeros naturais m e n e retorna o produto de todos os nmeros no intervalo [m, n]: m (m + 1) (n 1) n

33/39

Exerccios (cont.)

Exerccio 9 Usando a funo denida no exerccio 8, escreva uma denio no recursiva para calcular o fatorial de um nmero natural. Exerccio 10 Dena uma funo recursiva para calcular a soma de dois nmeros inteiros, sem usar os operadores + e -. Utilize as funes succ e pred da biblioteca, que calculam respectivamente o sucessor e o antecessor de um valor. Exerccio 11 Dena uma funo recursiva para calcular a potncia de um nmero, considerando que o expoente um nmero natural. Utilize o mtodo das multiplicaes sucessivas.

34/39

Exerccios (cont.)

Exerccio 12 A raiz quadrada inteira de um nmero inteiro positivo n o maior nmero inteiro cujo quadrado menor ou igal a n. Por exemplo, a raiz quadrada inteira de 15 3, e a raiz quadrada inteira de 16 4. Dena uma funo recursiva para calcular a raiz quadrada inteira. Exerccio 13 Dena duas funes recursivas que calculam o quociente e o resto da diviso inteira de dois nmeros inteiros usando subtraes sucessivas.

35/39

Exerccios (cont.)

Exerccio 14 Dena uma funo recursiva para calcular o mximo divisor comum de dois nmeros inteiros no negativos a e b, usando o algoritmo de Euclides:

mdc(a, b) =

se b = 0, se b > 0, se b < 0

mdc(b, a mod b) mdc(a, b)

Nota: o preldio j tem a funo gcd :: Integral a => a -> a -> a que calcula o mximo divisor comum de dois nmeros integrais.

36/39

Exerccios (cont.)

Exerccio 15 Faa uma denio recursiva para uma funo


maior :: (Integer -> Integer) -> Integer -> Integer

que recebe uma funo f e um nmero inteiro no negativo n, e retorna o maior dos valores
f 0, f 1, f 2, . . . , f (n-1), f n

Por exemplo, considerando a funo


g :: Integer -> Integer g x | even x = 2*x^2 - 3*x + 1 | otherwise = div (x^3) 2

temos
maior g 10 364

37/39

Exerccios (cont.)
Exerccio 16 Considere a seguinte funo para calcular o fatorial de um nmero:
fat n = fat n 1 where fat n x | n == 0 = x | n > 0 = fat (n-1) (n*x)

a) Mostre que fat 6 = 720. b) Compare o clculo de fat 6 com o clculo de fatorial 6 apresentado anteriormente. Qual verso da funo fatorial mais eciente: fatorial ou fat? Explique.

Exerccio 17 Dena uma funo com recursividade de cauda para calcular o n-simo (n 0) nmero de Fibonacci.
38/39

Fim

39/39