principais paradigmas de programação - slides – jarley...

38

Upload: vutuong

Post on 12-Dec-2018

218 views

Category:

Documents


0 download

TRANSCRIPT

Principais paradigmas de programação

Programação imperativa

Programação funcional

Programação lógica

Programação OO

Programação Imperativa

É o paradigma mais usado.

Programas são definidos através de sequências de comandos.

Ocorre mutação de variáveis (atribuição).

Laços são as estruturas básicas de controle.

Programação OO: dependência do paradigma imperativo.

Programação Funcional

Programas são expressões que eventualmente retornam um valor.

Programação é feita através de valores imutáveis e operações envolvendo esses valores.

Sem variáveis mutáveis, sem atribuição, sem laços e outras estruturas de controle imperativas.

Funções como mecanismo básico de abstração. Funções como valores que podem ser produzidos,

consumidos e combinados. Proximidade com a matemática -> facilita o

trabalho dos compiladores

Introdução à Programação Funcional

Origem do paradigma

Alonzo Church and Haskell Curry.

Década de 30: Alonzo Church desenvolveu as bases do Cálculo Lambda.

Formalismo matemático para definir e invocar funções

Introdução à Programação Funcional

Haskell Curry desenvolveu os princípios da lógica combinatória

Mostra como combinar funções para representar tarefas de computação.

Introdução à Programação Funcional

Expansão do paradigma

Permite estruturar computações com efeitos colaterais (que mudam o estado do programa) separados do código.

Objetivo do Paradigma Funcional:

Escrever código sem efeitos colaterais

Introdução à Programação Funcional

Efeitos colaterais Na matemática, uma função possui efeito colateral se

ela realiza alguma operação diferente do cálculo de seu retorno

Qualquer operação além do retorno altera o estado do programa.

Exemplos: Modificar uma variável dentro da função;

Modificar uma estrutura de dados;

Ler uma entrada digitada pelo usuário;

Imprimir alguma informação na console;

...

Introdução à Programação Funcional

Conceito:

Funções sem efeitos colaterais: não alteram o estado do programa.

São chamadas de funções “puras”.

Vantagens no uso de funções puras:

Facilidade para analisar , testar e depurar programas.

As variáveis não mudam!

Facilita a programação concorrente.

Introdução à Programação Funcional

Conceito:

Transparência referencial

Capacidade de referenciar uma função pura através da computação de seu retorno.

Exemplo:

y = sin(x)

Ao longo do programa, podemos fazer referência a y no lugar de sin(x).

Introdução à Programação Funcional

Princípios da Programação Funcional Imutabilidade de Estado

Funções de Primeira Classe

Funções de Alta Ordem

Funções Puras e Efeitos Colaterais

Recursividade

Avaliação Não-estrita

Programação declarativa

Imutabilidade de Estado

Começando com um exemplo

O que faz o trecho de código acima? O que pode dar errado nesse código?

int s = 0;

for (int i = 0; i < data.length; i++) {

s += data[i];

}

Imutabilidade de Estado

Se algum processo externo ao trecho alterar algum elemento de data, ocorrerá um efeito colateral.

A variável data deve ser tratada como imutável.

Após a sua definição, não serão permitidas alterações em seus valores

int s = 0;

for (int i = 0; i < data.length; i++) {

s += data[i];

}

Imutabilidade de Estado

Valores imutáveis

Variáveis e objetos imutáveis são thread-safe

Não alteram o estado do programa

A maioria das linguagens de programação não fazem

distinção entre um valor (o conteúdo armazenado na

memória) e a variável ao qual o valor se refere.

Os compiladores conseguem otimizar com maior eficiência

objetos imutáveis

Imutabilidade de Estado

Por que precisamos de variáveis imutáveis?

Para facilitar a programação concorrente Várias threads alterando

o mesmo valor -> necessidade de sincronização

Para facilitar os testes Não é necessário

acompanhar e testar as alterações na variável

Imutabilidade de Estado

Entretanto...

Algum nível de mutação é inevitável.

Exemplo: rotinas de I/O

Como resolver?

Encapsular as partes dos programas que são mutáveis.

Padrões de projetos Actor e Software Transactional Memory.

Imutabilidade de Estado

Como resolver?

Em Java

int s = 0;

for (int i = 0; i < data.Length; i++) {

s += data[i];

}

final int data[];

int s = 0;

for (int i = 0; i < data.length; i++) {

s += data[i];

}

Imutabilidade de Estado

Em Scala (versão 1)

Outra versão

var s = 0

for(i <- data) s+=i

var s = 0

data.foreach(i => s+=i)

Funções com valores de 1ª classe

Método tradicional de passagem de parâmetros em Java:

Passar valores primitivos ou objetos.

Valores primitivos e objetos são valores de 1ª classe em Java.

É possível passar um método como parâmetro de outro método?

Qual a diferença entre um método e uma função?

Funções com valores de 1ª classe

Métodos Funções

Função não é “amarrada” com nenhuma classe ou objeto;

Pode ser invocada em qualquer parte do programa;

Bloco de código pertencente obrigatoriamente à uma classe;

Só pode ser invocado no contexto da própria classe;

Java só possui métodos -> não são valores de primeira classe!

(i) Não é possível passar um método como parâmetro, (ii) retornar um método de outro método ou (iii) atribuir um método como valor de uma variável

Funções com valores de 1ª classe

Em programação funcional

Uma função pode ser composta com outras funções;

Uma função pode ser passada como parâmetro de outra função;

Uma função pode retornar outra função como valor;

Uma função pode ser atribuída a uma variável como valor.

Funções denotam valores de 1ª classe

Funções com valores de 1ª classe

Exemplos da matemática Seja f a função f(x)=x+1 e g a função g(x)=x2+x

Construções válidas:

(i) f(g(x)), g(f(x)), f(f(x)), ...

(ii) f(g(x))(10)(funções se associam à direita)

(iii) f(x)= y (y é imutável)

Lambdas e Closures

Em linguagens funcionais, a legibilidade do código pode ser melhorada através de funções anônimas, ou lambdas.

Função anônima: é uma expressão cujo resultado é avaliado por uma função.

A função é definida sem um nome

Exemplo: (x,y) -> x+y

Qual o tipo de (x,y)?

Solução: inferência de tipos

Lambdas e Closures

Recurso avançado de linguagens funcionais

Um bloco de código pode fazer referência à variáveis livres.

Variável livre: não é passada como argumento ou definida localmente (no escopo do bloco).

Em Java: inner classes são exemplos de variáveis livres.

Um closure é a formação de um bloco de código com as características acima

Funções de alta ordem

Recurso avançado de linguagens funcionais São funções de 1ª classe que permitem a

passagem de outras funções como parâmetro ou retornam uma função. São chamadas de funções de alta ordem.

Exemplos típicos: Funções com operações sobre estrutura de dados:

Listas, pilhas, filas, árvores, etc.

Padrões de combinação de funções. Exemplo: f(g(x)), (succ(pred e))->e, (pred (succ e))->e

Efeitos colaterais

Mutação de estado é fonte de erros nos programas concorrentes -> provocam efeitos colaterais.

Na matemática, funções não possuem efeitos colaterais. Exemplo: a função sin(x) não altera o estado

externo do programa quando calculada. A função sin(x) e o valor retornado são “sinônimos”. É possível representar o resultado da chamada pela

própria chamada !

Os compiladores podem armazenar sin(x)ao invés do valor retornado pela função.

Efeitos colaterais

Estilo de programação: criação de funções puras.

Permitir que a função só possa alterar valores internamente

Em programação funcional, uma função não deve alterar variáveis globais.

Recursão

Em linguagens tipicamente funcionais um loop clássico não é permitido:

for (int i=0; i<x; i++ { ...

Motivo: a variável i é mutável.

Solução: usar recursão para iterar sobre valores.

Recursão

Resolvendo o problema imutabilidade com recursão

int s = 0;

for (int i = 0; i < data.Length; i++) {

s += data[i];

}

Recursão

Resolvendo o problema imutabilidade com recursão

// versão com Array

def somaArray(x:Array[Int], size:Int):Int = {

if (size == 1)

return x(0)

else

return x(size-1) + somaArray(x, size-1)

}

// Versão com List

def somaLista(xs: List[Int]): Int = {

if(xs.isEmpty) 0

else xs.head + somaLista(xs.tail)

}

Recursão

Resolvendo o problema imutabilidade com recursão

// versão com List e operação de redução

x.foldLeft(0)(_+_)

// Obs.: em Scala, o mais indicado seria usar o

// método sum da classe List

// Exemplo: x.sum

Exercício 1

Para relembrar e praticar o uso de algoritmos recursivos, resolva os exercícios abaixo:

1. Crie um algoritmo recursivo para imprimir os elementos de um array com N posições na ordem crescente.

2. Crie um algoritmo recursivo para calcular o valor de xy (x elevado a y), onde x e y são inteiros.

3. Dada uma estrutura de dados do tipo pilha de tamanho N, escreva um algoritmo recursivo para retirar da pilha o elemento i, onde 1 ≤ 𝑖 ≤ 𝑁.

Avaliação não-estrita de funções

Em outros paradigmas de programação, devemos atribuir um valor para uma variável, logo após a sua declaração. Exemplo (em Java): int i =10;

Chamado de avaliação estrita.

No paradigma funcional podemos utilizar variáveis que não foram computadas após a sua declaração. O valor da variável será avaliado sob demanda.

Chamado de avaliação não-estrita.

Avaliação não-estrita de funções

Exemplo de aplicação: Vamos usar a função range em Python 2.x:

Os 10 valores da lista são atribuídos logo após a sua declaração (avaliação estrita)

Imagine agora uma lista gigante, com bilhões de elementos.

Toda a lista é alocada na memória logo após a sua atribuição.

>>> r = range(10) >>> print r [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Avaliação não-estrita de funções

Exemplo de aplicação: A função xrange em Python 2.7 funciona com

avaliação não-estrita:

Os valores de r são atribuídos sob demanda, em tempo de execução.

Para imprimir a lista:

>>> r = xrange(10)

>>> print r

xrange(10)

>>> lst = [x for x in r]

>>> print(lst)

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Avaliação não-estrita de funções

Na matemática, existem inúmeros conjuntos infinitos Exemplo: números inteiros

Esses conjuntos só podem ser representados de forma simbólica. Não é possível escrever um programa que imprima

todos os número inteiros!

Como lidamos com esses conjuntos? Subconjuntos de valores finitos

Cada subconjunto é avaliado sob demanda, de forma não-estrita.

Programação declarativa

Linguagens funcionais são declarativas por natureza.

Para entender, vamos olhar a função Fatorial

factorial(n) = 1 if n = 1

n * factorial(n-1) if n > 1

Programação declarativa

Solução Declarativa (Java) Solução Imperativa (Java)

public static long declarativeFactorial(int n) { assert n > 0 : "Argument must be greater than 0"; I f (n == 1) return 1; else return n * declarativeFactorial(n-1); }

public static long imperativeFactorial(int n) { assert n > 0 : "Argument must be greater than 0"; Long result = 1; for (int i = 2; i<= n; i++) { result *= i; } return result; }