• Nenhum resultado encontrado

Programação Funcional Aulas 9, 10 & 11

N/A
N/A
Protected

Academic year: 2021

Share "Programação Funcional Aulas 9, 10 & 11"

Copied!
14
0
0

Texto

(1)

Programa¸c˜

ao Funcional – Aulas 9, 10 & 11

Sandra Alves

DCC/FCUP

2015/16

1

Programas interativos

Motiva¸c˜ao

At´e agora apenas escrevemos programas que efetuam computa¸c˜ao pura, i.e., transforma¸c˜oes funcionais entre valores.

Vamos agora ver como escrever programas interativos: • lˆem informa¸c˜ao do teclado, ficheiros, etc.;

• escrevem no terminal ou em ficheiros; • . . .

A¸c˜oes de I/O

Introduzimos um novo tipo IO () para a¸c˜oes que, se forem executadas, fazem entrada/sa´ıda de dados. Exemplos:

putChar ’A’ :: IO () -- imprime um ’A’

putChar ’B’ :: IO () -- imprime um ’B’

(2)

Encadear a¸c˜oes

Podemos combinar duas a¸c˜oes de I/O usando o operador de sequencia¸c˜ao: (>>) :: IO () -> IO () -> IO ()

Exemplos:

(putChar ’A’ >> putChar ’B’) :: IO () -- imprimir ”AB” (putChar ’B’ >> putChar ’A’) :: IO () -- imprimir ”BA” Note que >> ´e associativo mas n˜ao ´e comutativo!

Em alternativa podemos usando a nota¸c˜ao-do: putChar ’A’ >> putChar ’B’ >> putChar ’C’ =

do {putChar ’A’; putChar ’B’; putChar ’C’}

Podemos omitir os sinais de pontua¸c˜ao usando a indenta¸c˜ao: do putChar ’A’

putChar ’B’ putChar ’C’

Execu¸c˜ao

Para efetuar as a¸c˜oes de I/O definimos um valor main no m´odulo Main. module Main where

main = do putChar ’A’ putChar ’B’

Compilar e executar: $ ghc Main.hs -o prog $ ./prog

AB$

Tamb´em podemos efetuar a¸c˜oes IO diretamente no ghci: Prelude> putChar ’A’ >> putChar ’B’

ABPrelude>

Definir novas a¸c˜oes

Vamos agora definir novas a¸c˜oes de I/O combinando a¸c˜oes mais simples.

Exemplo: definir putStr usando putChar recursivamente. putStr :: String -> IO ()

putStr [] = ??

putStr (x:xs) = putChar x >> putStr xs Como completar?

(3)

A¸c˜ao vazia

putStr :: String -> IO () putStr [] = return ()

putStr (x:xs) = putChar x >> putStr xs

return () ´e a a¸c˜ao vazia: se for efetuada, n˜ao faz nada. Mais geralmente

IO a ´e o tipo de a¸c˜oes que, se forem executadas, fazem entrada/sa´ıda de dados e devolvem um valor de tipo a.

Exemplos:

putChar ’A’ :: IO () -- escrever um ’A’; resultado vazio getChar :: IO Char -- ler um caracter; resultado Char A¸c˜oes IO pr´e-definidas

getChar :: IO Char -- ler um caracter

getLine :: IO String -- ler uma linha

getContents :: IO String -- ler toda a entrada padr˜ao putChar :: Char -> IO () -- escrever um carater putStr :: String -> IO () -- escrever uma linha de texto putStrLn :: String -> IO () -- idem com mudan¸ca de linha print :: Show a => a -> IO () -- imprimir um valor

return :: a -> IO a -- a¸c˜ao vazia

Combinando leitura e escrita

Usamos <- para obter valores retornados por uma a¸c˜ao I/O. Exemplo: ler e imprimir caracteres at´e obter um fim-de-linha. main :: IO ()

main = do x<-getChar putChar x

if x==’\n’ then return () else main Outro exemplo:

boasvindas :: IO ()

boasvindas = do putStr "Como te chamas? " nome <- getLine

(4)

Valores de retorno

Podemos usar return para definir valores de retorno de a¸c˜oes. boasvindas :: IO String

boasvindas

= do putStr "Como te chamas? " nome <- getLine

putStr ("Bem-vindo, " ++ nome ++ "!\n") return nome

Outro exemplo: definir getLine usando getChar. getLine :: IO String getLine = do x<-getChar if x==’\n’ then return [] else do xs<-getLine return (x:xs) Jogo Hi-Lo

Exemplo maior: um jogo de perguntas-respostas.

• o computador escolhe um n´umero secreto entre 1 e 100; • o jogador vai fazer tentativas de advinhar;

• para cada tentativa o computador diz se ´e alto ou baixo; • a pontua¸c˜ao final ´e o n´umero de tentativas.

Tentativa? 50 Demasiado alto! Tentativa? 25 Demasiado baixo! Tentativa? 35 Demasiado alto! Tentativa? 30 Demasiado baixo! Tentativa? 32 Acertou em 5 tentativas.

Vamos decompor em duas partes:

main escolhe o n´umero secreto e inicia o jogo;

jogo fun¸c˜ao recursiva que efetua a sequˆencia perguntas-respostas. Programa

module Main where

import Data.Char(isDigit) import System.Random(randomRIO)

(5)

main = do x <- randomRIO (1,100) -- escolher n´umero alet´orio n <- jogo 1 x -- come¸car o jogo

putStrLn ("Acertou em " ++ show n ++ " tentativas")

jogo :: Int -> Int -> IO Int

jogo n x -- n: tentativas, x: n´umero secreto

= do { putStr "Tentativa? " ; str <- getLine

; if all isDigit str then let y = read str in if y>x then

do putStrLn "Demasiado alto!"; jogo (n+1) x else if y<x then

do putStrLn "Demasiado baixo!"; jogo (n+1) x else return n

else do putStrLn "Tentativa inv´alida!"; jogo n x }

A¸c˜oes s˜ao valores

As a¸c˜oes IO s˜ao valores de primeira classe: • podem ser argumentos ou resultados de fun¸c˜oes; • podem passados em listas ou tuplos;

• . . .

Isto permite muita flexibilidade ao combinar a¸c˜oes.

Exemplo: uma fun¸c˜ao para efetuar uma lista de a¸c˜oes por ordem. seqn :: [IO a] -> IO ()

seqn [] = return () seqn (m:ms) = m >> seqn ms

Exemplos de uso:

> seqn [putStrLn s | s<-["ola", "mundo"]] ola

mundo

> seqn [print i | i<-[1..5]] 1 2 3 4 5 Sum´ario

• Programas reais necessitam de combinar intera¸c˜ao e computa¸c˜ao pura

(6)

• A nota¸c˜ao-do e o tipo IO ´e usada para: – ler e escrever no terminal e em ficheiros; – estabelecer comunica¸c˜oes de rede;

– servi¸cos do sistema operativo (ex: obter data e hora do rel´ogio de sistema); – A nota¸c˜ao-do pode usada para outras computa¸c˜oes n˜ao puramente funcionais:

∗ estado, n˜ao-determinismo, exce¸c˜oes, etc.

2

Defini¸

ao de tipos

Declara¸c˜oes de sin´onimos

Podemos dar um nome novo a um tipo existente usando uma declara¸c˜ao de sin´onimo. Exemplo (do prel´udio-padr˜ao):

type String = [Char]

As declara¸c˜oes de sin´onimos s˜ao usadas para melhorar legibilidade de programas. Exemplo:

type Pos = (Int,Int) -- coluna,linha

type Cells = [Pos] -- col´onia

Assim podemos escrever

isAlive :: Cells -> Pos -> Bool em vez de

isAlive :: [(Int,Int)] -> (Int,Int) -> Bool

As declara¸c˜oes de sin´onimos tamb´em podem ter parˆametros. Exemplo: associa¸c˜oes entre chaves e valores.

type Assoc ch v = [(ch,v)] -- tabela de associa¸c˜oes idades :: Assoc String Int

idades = [("Sara", 39), ("Jo~ao", 27), ("Maria", 19)] emails :: Assoc String String

emails = [("Sandra", "sandra@dcc.fc.up.pt"), ("Jo~ao", "joao@gmail.com")]

Os sin´onimos podem ser usados noutras defini¸c˜oes: type Pos = (Int,Int)

type Cells = [Pos] -- OK

Mas n˜ao podem ser usados recursivamente:

(7)

Declara¸c˜oes de novos tipos

Podemos definir novos tipos de dados usando declara¸c˜oes data. Exemplo (do prel´udio-padr˜ao):

data Bool = True | False

• A declara¸c˜ao data enumera as alternativas separadas por barras verticais. • Cada alternativa deve ter um construtor (ex.: True e False).

• O nome dos tipos e construtores deve ser come¸car por uma letra mai´uscula. • Cada construtor s´o pode ser usado num ´unico tipo.

Podemos definir fun¸c˜oes sobre novos tipos usando padr˜oes.

Exemplo: um tipo para as dire¸c˜oes ortogonais (esquerda, direita, cima, baixo). data Dir = Esq | Dir | Cima | Baixo

Vamos definir algumas fun¸c˜oes. . .

contraria :: Dir -> Dir -- dire¸c˜ao contr´aria contraria Esq = Dir

contraria Dir = Esq contraria Cima = Baixo contraria Baixo = Cima

mover :: Dir -> Pos -> Pos -- deslocar numa dire¸c˜ao mover Esq (x,y) = (x-1,y)

mover Dir (x,y) = (x+1,y) mover Cima (x,y) = (x,y+1) mover Baixo (x,y)= (x,y-1) Construtores com parˆametros

Os construtores podem tamb´em ter parˆametros. Exemplo:

data Figura = Circ Float -- raio

| Rect Float Float -- largura, altura

quadrado :: Float -> Figura quadrado h = Rect h h area :: Figura -> Float area (Circ r) = pi*r^2 area (Rect w h) = w*h

(8)

• Os parˆametros podem ser de tipos diferentes • Podemos usar os construtores de duas formas:

como fun¸c˜oes para construir um valor Circ :: Float -> Figura

Rect :: Float -> Float -> Figura em padr˜oes no lado esquerdo de equa¸c˜oes

area (Circ r) = pi*r^2 area (Rect w h) = w*h Igualdade e convers˜ao em texto

Por omiss˜ao um novo tipo n˜ao tem m´etodos de igualdade ou convers˜ao para texto. O interpretador d´a erro se tentarmos mostrar ou comparar valores:

> Circ 2

ERROR: No instance for (Show Figura)... > Rect 2 1 == Rect 1 2

ERROR: No instance for (Eq Figura)...

Podemos definir igualdade e convers˜ao para texto automaticamente usando “deriving”: data Figura = Circ Float

| Rect Float Float deriving (Eq, Show) Exemplo de uso:

> Circ 2 Circ 2.0

> Rect 2 1 == Rect 1 2 False

A igualdade ´e sint´atica: dois valores s˜ao iguais se e s´o se tˆem o mesmo construtor e argumentos. Novos tipos com parˆametros

As declara¸c˜oes de novos tipos tamb´em podem ter parˆametros. Exemplo:

data Maybe a = Nothing | Just a -- do prel´udio-padr˜ao safediv :: Int -> Int -> Maybe Int

safediv _ 0 = Nothing

safediv n m = Just (n‘div‘m) safehead :: [a] -> Maybe a safehead [] = Nothing

(9)

Tipos recursivos

As declara¸c˜oes data podem ser recursivas. Exemplo: os n´umeros naturais.

data Nat = Zero | Suc Nat Alguns valores de Nat:

Zero -- zero

Suc Zero -- um

Suc (Suc Zero) -- dois

Suc (Suc (Suc Zero)) -- trˆes

. . .

Em geral: n ´e obtido aplicado n vezes Succ a Zero.

Suc (Suc (... (Suc Zero)...)) -- n aplica¸c˜oes Usando recurs˜ao, podemos definir fun¸c˜oes que convertem entre inteiros e naturais:

int2nat :: Int -> Nat int2nat 0 = Zero

int2nat n | n>0 = Suc (int2nat (n-1)) nat2int :: Nat -> Int

nat2int Zero = 0

nat2int (Suc n)= 1+nat2int n

Podemos usar as fun¸c˜oes de convers˜ao para somar naturais. add :: Nat -> Nat -> Nat

add n m = int2nat (nat2int n + nat2int m)

Em alternativa, podemos definir a soma usando recurs˜ao sobre naturais. add :: Nat -> Nat -> Nat

add Zero m = m

add (Suc n) m = Suc (add n m)

Estas duas equa¸c˜oes traduzem as seguintes igualdades alg´ebricas: 0 + m = m

(1 + n) + m = 1 + (n + m) Exemplo:

add (Suc (Suc Zero)) (Suc Zero) =

Suc (add (Suc Zero) (Suc Zero)) =

Suc (Suc (add Zero (Suc Zero))) =

(10)

´

Arvores sint´aticas

Podemos representar express˜oes por uma ´arvore sint´atica em que os operadores s˜ao os n´os e as constantes s˜ao as folhas. Exemplo: 1 + 2 × 3 ' +  1 × ~~  2 3

As ´arvores podem ser representadas em Haskell por um tipo recursivo.

data Expr = Val Int -- constante

| Soma Expr Expr -- n´o +

| Mult Expr Expr -- n´o ×

A ´arvore no slide anterior ´e:

Soma (Val 1) (Mult (Val 2) (Val 3))

Exemplos de fun¸c˜oes sobre ´arvores de express˜oes.

-- contar o n´umero de folhas tamanho :: Expr -> Int tamanho (Val n) = 1

tamanho (Soma e1 e2) = tamanho e1 + tamanho e2 tamanho (Mult e1 e2) = tamanho e1 + tamanho e2 -- calcular o valor

valor :: Expr -> Int valor (Val n) = n

valor (Soma e1 e2) = valor e1 + valor e2 valor (Mult e1 e2) = valor e1 * valor e2

´

Arvores bin´arias

Tamb´em podemos usar ´arvores bin´arias para facilitar a organiza¸c˜ao e pesquisa de informa¸c˜ao. 5 xx '' 3  7  1 4 6 9

Podemos representar ´arvores bin´arias de inteiros por um tipo recursivo. data Arv = Folha Int

(11)

A ´arvore no slide anterior seria representa por: No (No (Folha 1) 3 (Folha 4))

5

(No (Folha 6) 7 (Folha 9))

Podemos agora definir uma fun¸c˜ao recursiva para procurar um valor numa ´arvore. ocorre :: Int -> Arv -> Bool

ocorre m (Folha n) = n==m ocorre m (No esq n dir) = (n==m ||

ocorre m esq || ocorre m dir)

Numa ´arvore ordenada todos os n´os tˆem valores inferiores na sub-´arvore esquerda e superiores na sub-´

arvore direira. Nesse caso podemos simplificar a pesquisa: ocorre’ :: Int -> Arv -> Bool

ocorre’ m (Folha n) = n==m

ocorre’ m (No esq n dir) | n==m = True

| m<n = ocorre’ m esq | m>n = ocorre’ m dir

Esta defini¸c˜ao ´e mais eficiente: percorre apenas os n´os num caminho da raiz at´e uma folha em vez de todos os n´os da ´arvore.

3

Verificador de tautologias

Proposi¸c˜oes l´ogicas

Uma proposi¸c˜ao l´ogica ´e construida apartir de: constantes T, F (verdade e falsidade)

vari´aveis a, b, c, . . . conectivas l´ogicas ∧, ∨, ¬, =⇒ parˆentesis (, ) Exemplos: a ∧ ¬b a ∧ ((¬a) =⇒ F ) (¬(a ∨ b)) =⇒ ((¬a) ∧ (¬b)) Tabelas de verdade das conectivas

a b a ∧ b F F F T F F F T F T T T a b a ∨ b F F F T F T F T T T T T a ¬a F T T F a b a =⇒ b F F T T F F F T T T T T

(12)

Tautologias

Uma proposi¸c˜ao cujo valor ´e verdade para qualquer atribui¸c˜ao de valores `as vari´aveis diz-se uma tautologia.

Exemplo:

a ¬a a ∨ ¬a

F T T

T F T

Conclus˜ao: a ∨ ¬a ´e uma tautologia. Representa¸c˜ao de proposi¸c˜oes

Vamos definir um tipo recursivo para representar proposi¸c˜oes.

data Prop = Const Bool -- constantes

| Var Char -- vari´aveis

| Neg Prop -- nega¸c˜ao

| Conj Prop Prop -- conjun¸c˜ao

| Disj Prop Prop -- disjun¸c˜ao

| Impl Prop Prop -- implica¸c˜ao

deriving (Eq,Show) Exemplo: a proposi¸c˜ao

a =⇒ ((¬a) =⇒ F ) ´

e representada como

Impl (Var ’a’) (Impl (Neg (Var ’a’)) (Const False)) Associa¸c˜ao de valores a vari´aveis

Para atribuir valores de verdade `as vari´aveis vamos usar uma lista de associa¸c˜oes. Exemplo: a atribui¸c˜ao    a = T b = F c = T ´

e representada pela lista

[(’a’,True),(’b’,False),(’c’,True)] Definimos:

• listas de associa¸c˜oes entre chaves e valores;

• uma fun¸c˜ao para procurar o valor associado a uma chave.

type Assoc ch v = [(ch,v)]

find :: Eq ch => ch -> Assoc ch v -> v

find ch assocs = head [v | (ch’,v)<-assocs, ch==ch’] ´

(13)

Calcular o valor duma proposi¸c˜ao

Vamos definir o valor de verdade de uma proposi¸c˜ao por recurs˜ao. O primeiro argumento ´e uma atribui¸c˜ao de valores `as vari´aveis. type Atrib = Assoc Char Bool

valor :: Atrib -> Prop -> Bool valor s (Const b) = b

valor s (Var x) = find x s

valor s (Neg p) = not (valor s p)

valor s (Conj p q) = valor s p && valor s q valor s (Disj p q) = valor s p || valor s q valor s (Impl p q) = not (valor s p) || valor s q Gerar atribui¸c˜oes `as vari´aveis

• Para n vari´aveis distintas h´a 2n linhas na tabela de verdade.

• Como obter todas as atribui¸c˜oes de forma sistem´atica?

• Vamos escrever uma fun¸c˜ao para gerar todas as sequˆencias de n boleanos (cf. exerc´ıcio 46): bits :: Int -> [[Bool]]

Exemplo, as sequˆencias de comprimento 3 (trˆes vari´aveis): F F F F F T F T F F T T T F F T F T T T F T T T                          bits 3

Podemos decompor em duas c´opias da tabela para 2 vari´aveis com uma coluna extra: F F F F F F F T T F T T          bits 2 T T T T F F F T T F T T          bits 2

Em geral: vamos gerar as sequˆencias de forma recursiva. bits :: Int -> [[Bool]]

bits 0 = [[]]

bits n = [b:bs | bs<-bits (n-1), b<-[False,True]]

(14)

vars :: Prop -> [Char] vars (Const _) = [] vars (Var x) = [x] vars (Neg p) = vars p

vars (Conj p q) = vars p ++ vars q vars (Disj p q) = vars p ++ vars q vars (Impl p q) = vars p ++ vars q

A fun¸c˜ao seguinte gera todas as as atribui¸c˜oes de vari´aveis duma proposi¸c˜ao: atribs :: Prop -> [Atrib]

atribs p = map (zip vs) (bits (length vs)) where vs = nub (vars p)

(A fun¸c˜ao nub da biblioteca Data.List remove repetidos.) Verificar tautologias

Uma proposi¸c˜ao ´e tautologia se e s´o se for verdade para todas as atribui¸c˜oes de vari´aveis. tautologia :: Prop -> Bool

tautologia p = and [valor s p | s<-atribs p] Alguns exemplos:

> tautologia (Var ’a’) False

> tautologia (Impl (Var ’p’) (Var ’p’)) True

> tautologia (Disj (Var ’a’) (Neg (Var ’a’)) True

Exerc´ıcios

1. Escrever uma fun¸c˜ao que calcula a lista das atribui¸c˜oes que tornam uma proposi¸c˜ao falsa (i.e. uma lista de contra-exemplos).

Referências

Documentos relacionados

O(s) analista(s) de investimento declara(m) que as opiniões contidas neste relatório refletem exclusivamente suas opiniões pessoais sobre a companhia e seus valores mobiliários e

Chiampi (org.).. Cabe lembrar que, sobretudo quando escrevia a sua chamada “literatura para o povo”, Tolstói estava já bastante próximo do mundo rural, observando a maneira como

Para obter informações sobre como configurar o Splash RPX-ii para obter ótimos resultados de impressão em seu ambiente particular, consulte o Guia de impressão e o Guia de cores..

Mas os trabalhadores terminaram por forçar o Estado ou os e!llpreg~dores à concessão de horários mais reduzidos ou de salá- nos mais elevados. Se há êsse dever,

2º ENCONTRO Observação no grupo: sala de aula regular, aulas dos especialistas, recreio e demais propostas da unidade escolar (horta, Educação e Tecnologia, biblioteca, GAP e

Para cada região são mostrados os quadros das ajudas por programa e por área do sector, no ano 2006, e o da execução financeira, em todo o período 2000- 2006. Todas as regiões

• Ausência de qualquer indício de fratura radicular. Figura 2: Radiografias representativas dos casos clínicos selecionados para o estudo. Os pacientes foram divididos em três

Por gentileza, coloque sua dúvida técnica acessando: fórum http://www.cadguru.com.br/perguntas-e-respostas/ para que tanto os usuários, como a equipe CADguru e os autores dos