Programação Funcional Aula 1 Introdução José Romildo Malaquias Departamento de Computação Universidade Federal de Ouro Preto 2011.2 1/32 1 Programação Funcional 2 Algumas características de Haskell 3 Antecedentes históricos 4 Experimentando Haskell 2/32 A Crise de Software I Como podemos lidar com o tamanho e a complexidade dos programas de computador modernos? I Como podemos reduzir o tempo e o custo de desenvolvimento do programas? I Como podemos aumentar nossa confiança de que os programas já concluídos funcionam corretamente? 3/32 Linguagens de Programação Uma abordagem para a crise do software é a concepção de novas linguagens de programação que: I permitam que programas sejam escritos de forma clara, concisa, e com um alto nível de abstração; I suportem componentes de software reutilizáveis; I incentivem o uso de verificação formal; I permitam prototipagem rápida; I forneçam poderosas ferramentas de resolução de problemas. 4/32 Linguagens de Programação (cont.) As linguagens funcionais fornecem um quadro particularmente elegante para abordar estes objetivos. 5/32 Função I Função é um mapeamento de um ou mais argumentos em um resultado único. I Em Haskell uma função é definida usando uma equação que dá um nome para a função, um nome para cada um de seus argumentos, e um corpo que especifica como o resultado pode ser calculado em termos dos argumentos. I Exemplo: Uma função chamada double que recebe um número x como seu argumento, e produz o resultado x + x double x = x + x 6/32 Aplicação de função I Quando uma função é aplicada aos argumentos reais, o resultado é obtido pela substituição desses argumentos no corpo da função no lugar dos nomes dos argumentos. I Este processo pode produzir imediatamente um resultado que não pode ser mais simplificado, como por exemplo um número. I Mais comumente, no entanto, o resultado será uma expressão contendo outras aplicações de função, que devem, então, ser processadas da mesma maneira para produzir o resultado final. 7/32 Aplicação de função (cont.) I Exemplo: Apcação da função double no argumento 3 double 3 = { aplicando double } 3 + 3 = { aplicando + } 6 8/32 Aplicação de função (cont.) I Exemplo: Aplicação aninhada de double double (double 2) = { aplicando double interno } double (2 + 2) = { aplicando + } double 4 = { aplicando double } 4 + 4 = { aplicando + } 8 9/32 Aplicação de função (cont.) I Exemplo: Aplicação aninhada de double, calculada de outra maneira double (double 2) = { aplicando double externo } double 2 + double 2 = { aplicando o primeiro double } (2 + 2) + double 2 = { aplicando o primeiro + } 4 + double 2 = { aplicando double } 4 + (2 + 2) = { aplicando o segundo + } 4 + 4 = { aplicando + } 8 10/32 Aplicação de função (cont.) I Em geral, a ordem na qual funções são aplicadas em um cálculo não afeta o valor do resultado final, mas pode afetar o número de passos necessário, e pode afetar se o processo de cálculo termina. 11/32 O que é uma linguagem funcional? As opiniões divergem, e é difícil dar uma definição precisa, mas de um modo geral: I Programação funcional é um estilo de programação em que o método básico de computação é a aplicação de funções a argumentos. I Uma linguagem funcional é aquela que apoia e incentiva o estilo funcional. 12/32 O que é uma linguagem funcional? (cont.) Exemplo: Somando os inteiros 1 a 10 em C: total = 0; for (i = 1; i < 10; ++i) total = total + i; I O método de cálculo é atribuição de variável. I Em geral, linguagens de programação em que o método básico de computação consiste em mudar os valores armazenados em variáveis são chamadas de linguagens imperativas, pois os programas nestas linguagens são construídos a partir de instruções imperativas que especificam precisamente como o cálculo deve ser realizado. 13/32 O que é uma linguagem funcional? (cont.) Exemplo: Somando os inteiros 1 a 10 em Haskell: sum [1..10] I O método de cálculo é aplicação de função. 14/32 Algumas características de Haskell I Programas concisos I Sistema de tipos poderoso I Compreensões de lista I Funções recursivas I Funções de ordem superior I Efeitos monádicos I Avaliação lazy I Raciocínio sobre programas 15/32 Antecedentes históricos I Década de 1930: Alonzo Church desenvolve o cálculo lambda, uma teoria de funções simples, mas poderosa. 16/32 Antecedentes históricos (cont.) I Década de 1950: John McCarthy desenvolve Lisp, a primeira linguagem funcional, com algumas influências do cálculo lambda, mas mantendo as atribuições de variáveis. 17/32 Antecedentes históricos (cont.) I Década de 1960: Peter Landin desenvolve ISWIM, a primeira linguagem funcional pura, baseada fortemente no cálculo lambda, sem atribuições. 18/32 Antecedentes históricos (cont.) I Década de 1970: John Backus desenvolve FP, uma linguagem funcional que enfatiza funções de ordem superior e raciocínio sobre programas. 19/32 Antecedentes históricos (cont.) I Década de 1970: Robin Milner e outros desenvolvem ML, a primeira linguagem funcional moderna, que introduziu a inferência de tipos e tipos polimórficos. 20/32 Antecedentes históricos (cont.) I Décadas de 1970 e 1980: David Turner desenvolve uma série de linguagens funcionais com avaliação lazy, culminando com o sistema Miranda. 21/32 Antecedentes históricos (cont.) I 1987: Um comitê internacional de pesquisadores inicia o desenvolvimento de Haskell, uma linguagem funcional lazy padrão. 22/32 Antecedentes históricos (cont.) I 2003: O comitê publica o relatório Haskell 98, a definição de uma versão estável da linguagem Haskell. 23/32 Antecedentes históricos (cont.) I 2009: O comitê publica o relatório Haskell 2010, uma revisão da definição da linguagem Haskell. 24/32 Experimentando Haskell sum :: Num a => [a] -> a sum [] = 0 sum (x:xs) = x + sum xs sum [1, 2, 3] = { aplicando sum } 1 + sum [2, 3] = { aplicando sum } 1 + (2 + sum [3]) = { aplicando sum } 1 + (2 + (3 + sum [ ])) = { aplicando sum } 1 + (2 + (3 + 0)) = { aplicando + } 6 25/32 Experimentando Haskell (cont.) f [] = [] f (x:xs) = f ys ++ [x] ++ f zs where ys = [a | a <- xs, a <= x] zs = [b | b <- xs, b > x] ? 26/32 Experimentando Haskell (cont.) f [x] = { aplicando f } f [] ++ [x] ++ f [] = { aplicando f } [] ++ [x] ++ [] = { aplicando ++ } [x] 27/32 Experimentando Haskell (cont.) f [3, 5, 1, 4, 2] = { aplicando f } f [1, 2] ++ [3] ++ f [5, 4] = { aplicando f } (f [] ++ [1] ++ f [2]) ++ [3] ++ (f [4] ++ [5] ++ f []) = { aplicando f, propriedade anterior } ([] ++ [1] ++ [2]) ++ [3] ++ ([4] ++ [5] ++ []) = { aplicando ++ } [1, 2] ++ [3] ++ [4, 5] = { aplicando ++ } [1, 2, 3, 4, 5] 28/32 Experimentando Haskell (cont.) I O tipo de f é f :: Ord a => [a] -> [a] I Dada uma lista xs, f xs é a lista formada pelos elementos de xs em ordem crescente. I f implementa o algoritmo de ordenação quick sort. 29/32 Exercícios Exercício 1 Dê outro cálculo possível para o resultado de double (double 2). Exercício 2 Mostre que sum [x] = x para qualquer número x. Exercício 3 Defina uma função product que produza o produto de uma lista de números, e mostre, usando sua definição, que product [2,3,4] = 24 Exercício 4 Mostre como a definição da função qsort deve ser modificada para que ela produza uma versão de uma lista ordenada em ordem decrescente. 30/32 Exercícios (cont.) Exercício 5 Qual é o efeito de trocar <= por < na definição de qsort? Dica: considere o exemplo qsort [2,2,3,1,1]. 31/32 Fim 32/32