Fundamentos da Programação
• Entidade subjacente à computação
• Definimos a sequência de ações a serem executadas por um programa
• Para desenvolver programas adequados é essencial ter uma compreensão dos processos computacionais gerados pelos diferentes tipos de funções
• Uma função especifica a evolução de um processo computacional
• A função define, em cada instante, o comportamento do processo, especifica como construir cada estágio do processo a partir do estágio anterior
• Estudar a evolução do processo cuja evolução local é definida por uma função
fatorial(3)
Operações adiadas (suspensas)
Recursão linear
return 3 * factorial (2)
...
n 3
Quadro global
Quadro local a fatorial
n 2
Quadro local a fatorial
6
return 2 * factorial (1)n 1
Quadro local a fatorial
return 1 * factorial (0)n 0
Quadro local a fatorial
Recursão linear
def soma_elementos(lst): if lst == []: return 0 else: return lst[0] + \ soma_elementos(lst[1:])• Comportamento do processo computacional • Operações suspensas (adiadas)
• Criação de ambientes correspondentes à mesma função
• Expansão da memória necessária para a execução do processo (fase de crescimento)
• Diminuição da memória necessária (fase de contração)
• Processo recursivo
• Comportamento do processo computacional
• Criação de uma sequência de ambientes correspondentes à mesma função
• Expansão da memória necessária para a execução do processo (fase de crescimento)
• Diminuição da memória necessária (fase de contração)
• Processo recursivo
• O número ambientes cresce linearmente com um valor (um dos parâmetros da função)
• Processo recursivo linear
Recursão linear
fatorial(3)
Iteração linear
• Processo é caracterizado por um certo número de variáveis, as
variáveis de estado, juntamente com uma regra que especifica como
atualizá-las
• Fornecem uma descrição completa do estado do processo em cada momento
• Processo iterativo
• Processo é caracterizado por um certo número de variáveis, as
variáveis de estado, juntamente com uma regra que especifica como
atualizá-las
• Fornecem uma descrição completa do estado do processo em cada momento
• Processo iterativo
• O número de operações efetuadas cresce linearmente com uma grandeza associada aos argumentos da função
• Processo iterativo linear
Iteração linear
Recursão de cauda
• Será que podemos escrever a função recursiva fatorial que “regista” o valor já calculado de fatorial, tal como no processo iterativo linear,
Recursão de cauda
• Será que podemos escrever a função recursiva fatorial que “regista” o valor já calculado de fatorial, tal como no processo iterativo linear,
evitando as operações suspensas?
Recursão de cauda
• Será que podemos escrever a função recursiva fatorial que “regista” o valor já calculado de fatorial, tal como no processo iterativo linear,
evitando as operações suspensas?
• Usar um argumento adicional para o valor parcial de fatorial
def fatorial_rec(prod_ac, prox): if prox == 0:
return prod_ac else:
Recursão de cauda
fatorial_rec(1, 5) | fatorial_rec(5, 4) | | fatorial_rec(20, 3) | | | fatorial_rec(60, 2) | | | | fatorial_rec(120, 1) | | | | | fatorial_rec(120, 0) | | | | | return 120 | | | | return 120 | | | return 120 | | return 120 | return 120 return 120Recursão de cauda
def fatorial(n):
def fatorial_rec(prod_ac, prox): if prox == 0:
return prod_ac else:
return fatorial_rec(prod_ac * prox, prox - 1) return fatorial_rec(1, n)
Recursão de cauda
• Fazer o mesmo com a potenciadef potencia(x, n):
def potencia_rec(x, n_mult, prod_ac): if n_mult == 0:
return prod_ac else:
return potencia_rec(x, n_mult - 1, x * prod_ac) return potencia_rec(x, n, 1)
Recursão de cauda
• Fazer o mesmo com soma elementos
def soma_elementos(lst):
def soma_elementos_aux(lst, soma): if lst == []:
return soma else:
return soma_elementos_aux(lst[1:], lst[0] + soma) return soma_elementos_aux(lst, 0)
Recursão de cauda
• Processo recursivo• Não existem operações adiadas
• As funções que geram processos iterativos e as funções que utilizam a recursão de cauda partilham a característica de utilizarem variáveis de estado
• Alguns processadores de linguagens transformam automaticamente funções que utilizam recursão de cauda em funções que geram
processos iterativos, sendo comum dizer que este tipo de funções gera processos iterativos
Recursão em árvore
• Números de Fibonacci• Sequência de números descoberta pelo matemático italiano Leonardo Fibonacci (~1170-~1250)
• Cada termo, exceto os dois primeiros, é a soma dos dois anteriores. Os dois primeiros termos são, respetivamente, 0 e 1
Recursão em árvore
• Números de Fibonacci
• “Código secreto da natureza”, “regra universal da natureza” • Exemplo é a concha do nautilus
• Aparece em todos os lugares da natureza, incluindo flores, pinhas, furacões e até mesmo nas galáxias espirais no espaço
Recursão em árvore
def fib(n): if n == 0: return 0 elif n == 1: return 1 else:Recursão em árvore
fib(5) | fib(4) | | fib(3) | | | fib(2) | | | | fib(1) | | | | return 1 | | | | fib(0) | | | | return 0 | | | return 1 | | | fib(1) | | | return 1 | | return 2 | | fib(2) | | | fib(1) | | | return 1 | | | fib(0) | | | return 0 | | return 1 | return 3 | fib(3) | | fib(2) | | | fib(1) | | | return 1 | | | fib(0) | | | return 0 | | return 1 | | fib(1) | | return 1 | return 2 return 5Recursão em árvore
fib(5) | fib(4) | | fib(3) | | | fib(2) | | | | fib(1) | | | | return 1 | | | | fib(0) | | | | return 0 | | | return 1 | | | fib(1) | | | return 1 | | return 2 | | fib(2) | | | fib(1) | | | return 1 | | | fib(0) | | | return 0 | | return 1 | return 3 | fib(3) | | fib(2) | | | fib(1) | | | return 1 | | | fib(0) | | | return 0 | | return 1 | | fib(1) | | return 1 | return 2 return 5• A forma do processo não corresponde a nenhum dos padrões já estudados
• Apresenta um comportamento que se assemelha ao processo recursivo
• Tem fases de crescimento em que são criados novos ambientes correspondentes à função fib, seguidas por fases de contração em que alguns destes
ambientes desaparecem
• Existem múltiplas fases de crescimento e de
contração originadas pela dupla recursão que existe em fib
Recursão em árvore
Recursão em árvore
• Para evitar os cálculos repetidos, função que utiliza a recursão de cauda
• Como cada termo da sequência, exceto os dois primeiros, é a soma dos dois anteriores, para calcular um dos termos teremos de saber os dois termos anteriores
• No cálculo de um termo genérico 𝑓! os dois termos anteriores são 𝑓!"# e 𝑓!"$
• O próximo número de Fibonacci será 𝑓! = 𝑓!"# + 𝑓!"$ • Os dois últimos termos passam a ser 𝑓! e 𝑓!"#
Recursão em árvore
def fib_aux(f_n_1, f_n_2, cont): if ?:
return ?
else:
Recursão em árvore
def fib_aux(f_n_1, f_n_2, cont): if cont == 0:
return f_n_2 + f_n_1 else:
Recursão em árvore
def fib(n):
def fib_aux(f_n_1, f_n_2, cont): if cont == 0:
return f_n_2 + f_n_1 else:
return fib_aux(f_n_2 + f_n_1, f_n_1, cont-1) if n == 0: return 0 elif n == 1: return 1 else: return fib_aux (1, 0, n-2)
Recursão em árvore
Origina um processo recursivo linear em que não há duplicações de cálculos fib(5) fib_aux(1, 0, 3) | fib_aux(1, 1, 2) | | fib_aux(2, 1, 1) | | | fib_aux(3, 2, 0) | | | return 5 | | return 5 | return 5 return 5 return 5
Recursão em árvore
• A recursão em árvore é um processo inútil? • Não !