







Estude fácil! Tem muito documento disponível na Docsity
Ganhe pontos ajudando outros esrudantes ou compre um plano Premium
Prepare-se para as provas
Estude fácil! Tem muito documento disponível na Docsity
Prepare-se para as provas com trabalhos de outros alunos como você, aqui na Docsity
Encontra documentos específicos para os exames da tua universidade
Prepare-se com as videoaulas e exercícios resolvidos criados a partir da grade da sua Universidade
Responda perguntas de provas passadas e avalie sua preparação.
Ganhe pontos para baixar
Ganhe pontos ajudando outros esrudantes ou compre um plano Premium
Entrada e Saida em Haskell
Tipologia: Notas de estudo
1 / 13
Esta página não é visível na pré-visualização
Não perca as partes importantes!








Departamento de Computa¸c˜ao Universidade Federal de Ouro Preto Programa¸c˜ao Funcional Prof. Luc´ılia Figueiredo
Elton M. Cardoso Luc´ılia Figueiredo
30 de Junho de 2005
e escrita de caracteres em arquivos armazenados em disco. Outras formas mais complexas de intera¸c˜ao, tais como a comunica¸c˜ao atrav´es de redes ou a entrada e sa´ıda de dados via inter- faces gr´aficas, ser˜ao abordados em um curso mais avan¸cado. Consulte o manual da linguagem (www.haskell.org/definiton) para maiores detalhes.
Come¸camos com o cl´assico exemplo do programa Hello Word, que pode ser escrito, em Haskell, do seguinte modo:
module Main where main = putStrLn "Hello World!" Como resultado da execu¸c˜ao deste programa, ´e enviada uma mensagem ao sistema ope- racional, indicando que o texto passado como argumento para a fun¸c˜ao putStrLn deve ser impresso na tela (ou seja, no dispositivo de sa´ıda padr˜ao). As seguintes fun¸c˜oes podem ser usadas para imprimir um string na tela, sendo a ´unica diferen¸ca entre elas o fato de que a segunda inicia uma nova linha, depois de imprimir o string:
putStr :: String -> IO () putStrLn :: String -> IO () O tipo de retorno dessas fun¸c˜oes – IO () – indica que ´e executada uma a¸c˜ao de E/S e nenhum valor ´e retornado^1. Dizemos que fun¸c˜oes como putStr e putStrLn s˜ao fun¸c˜oes de E/S ou fun¸c˜oes de intera¸c˜ao, em contraposi¸c˜ao a fun¸c˜oes puras, tais como, por exemplo (++). Para ler uma linha de caracteres do teclado, podemos usar a fun¸c˜ao: getLine :: IO(String)
O tipo desta fun¸c˜ao indica que ela executa uma a¸c˜ao de entrada de dados e retorna um valor do tipo String.
Consideramos, at´e agora, a¸c˜oes individuais, tais como putStr e getLine. Entretanto, em um programa, usualmente necessitamos combinar a¸c˜oes de modo a implementar uma a¸c˜ao mais complexa. Suponha que queremos ler uma linha do teclado e imprimir essa linha na tela. Poder´ıamos tentar escrever:
putStr getLine -- Erro de tipo!! Mas isso n˜ao funciona! Porque? A fun¸c˜ao getLine tem tipo IO String e putStr espera um string puro como argumento, ao inv´es de uma a¸c˜ao de E/S que produz um string como resultado. Como dissemos anteriormente, Haskell distingue entre computa¸c˜oes puras e intera¸c˜oes por meio de tipos. Express˜oes de tipo String e IO String denotam objetos diferentes e Haskell evita que eles sejam confundidos. Quando desejamos combinar a¸c˜oes, devemos usar a nota¸c˜ao do, que tem a forma geral: (^1) Podemos associar o tipo () ao tipo void de Java.
do <comando 1> <comando 2> .. .
onde cada comando ´e uma a¸c˜ao de E/S, elementar ou composta. Quando estamos interessados em obter o resultado de uma a¸c˜ao, podemos ligar o resultado dessa a¸c˜ao a uma vari´avel v, usando o operador (<-) :: IO (a) -> a, da seguinte forma:
v <- <a¸c~ao>
Podemos agora combinar getLine e putStr do seguinte modo:
do input <- getLine putStr input
Como getLine tem tipo IO String, a vari´avel input denota o string lido por getLine, de tipo String. Podemos agora definir uma fun¸c˜ao que efetua a a¸c˜ao composta acima, ou seja, lˆe um string do teclado e ecoa esse string na tela:
ecoLine :: IO () ecoLine = do input <- getLine putStr input
Exerc´ıcio: Defina um programa que lˆe do teclado e imprime na tela, seguidamente, duas linhas de texto.
Assim como no caso de computa¸c˜oes, ´e ´util definir fun¸c˜oes que executam a¸c˜oes mais complexas, pela combina¸c˜ao de a¸c˜oes elementares. Por exemplo, suponha que desejamos definir uma fun¸c˜ao ask, que dada uma pergunta (na forma de um string), exibe essa pergunta na tela, lˆe a resposta digitada pelo usu´ario e retorna a resposta lida, como um resultado do tipo IO String. Essa fun¸c˜ao poderia ser definida do seguinte modo:
ask :: String -> IO String ask question = do putStr question getLine
Podemos usar essa fun¸c˜ao em um programa para ler o nome e o n´umero de matr´ıcula de um aluno, como a seguir:
leInt2 :: IO(Int) leInt2 = do putStr "Digite um valor inteiro: " n <- getLine return (read n)
Com base nessas defini¸c˜oes, poder´ıamos escrever o seguinte programa para ler dois n´umeros inteiros e imprimir as soma desses n´umeros:
main :: IO () main = do n1 <- leInt n2 <- leInt putStr "A soma ´e: " print (n1+n2)
As fun¸c˜oes read, readLn e print possuem as seguintes assinaturas:
read :: Read a => String -> a readLn :: Read a => a print :: Show a -> a -> IO ()
Ainda outra maneira de definir a fun¸c˜ao leInt seria:
leInt3 :: IO(Int) leInt3 = do putStr "Digite um valor inteiro: " readIO
A fun¸c˜ao readIO :: Read a => IO a ´e uma combina¸c˜ao de getLine e read, exceto que ela propaga uma exce¸c˜ao, caso o string lido por getLine n˜ao represente um valor num´erico. Veremos como uma exce¸c˜ao pode ser tratada, na se¸c˜ao 1.6, a seguir. Podemos tamb´em usar as fun¸c˜oes readLn e print para ler e imprimir quaisquer outros valores cujos tipos s˜ao instˆancias da classe Show. Por exemplo, o programa a seguir lˆe um valor de ponto flutuante x e imprime a lista de todos os valores de 0 a x, em intervalos de 0.1:
main :: IO () main = do putStr "Digite um valor: " x <- readLn print [0,0.1..x]
Duas outras fun¸c˜oes s˜ao definidas na linguagem para opera¸c˜oes de leitura a partir do dis- positivo de entrada padr˜ao:
getContents :: IO String interact :: (String -> String) -> IO ()
A fun¸c˜ao getContents retorna toda a entrada digitada pelo usu´ario como um ´unico string, que ´e lido de forma lazy, `a medida que requerido. A fun¸c˜ao interact tem como argumento uma fun¸c˜ao do tipo String -> String. Toda a entrada lida do dispositivo pardr˜ao ´e passada como argumento para essa fun¸c˜ao e o string resultante ´e impresso no dispositivo de sa´ıda padr˜ao. Por exemplo, o programa
main = interact (filter isUpper)
imprime na tela apenas as letras mai´usculas do string lido do teclado.
Exerc´ıcios:
Haskell faz interface com o mundo externo por meio de um sistema de arquivos abstrato – uma cole¸c˜ao de arquivos que podem ser organizados em diret´orios. Nomes de arquivos e diret´orios s˜ao objetos do tipo String, e podem especificar o path completo at´e o arquivo, ou apenas o nome do arquivo relativo ao diret´orio corrente. O formato do nome do arquivo corresponde ao utilizado no sistema operacional Unix. Arquivos podem ser abertos para leitura, escrita ou leitura/escrita. Essa opera¸c˜ao associa ao arquivo um handler (do tipo Handle), que ´e usado para posteriores referˆencias ao arquivo em opera¸c˜oes de leitura e escrita. Trˆes handlers s˜ao automaticamente associados a arquivos no in´ıcio da execu¸c˜ao de um programa: stdin – dispositivo de entrada padr˜ao, associado ao teclado; stdout – dispositivo de sa´ıda padr˜ao, associado ao console; e stderr – dispositivo de erro padr˜ao, tamb´em associado ao console. Algumas opera¸c˜oes b´asicas de leitura e escrita em carquivos, definidas no m´odulo IO, s˜ao apresentadas a seguir:
type File = String
writeFile :: File -> String -> IO () appendFile :: File -> String -> IO () readFile :: File -> IO String
A fun¸c˜ao writeFile cria um novo arquivo, com o nome especificado pelo primeiro argu- mento, e escreve nesse arquivo o string passado como segundo argumento. Caso o arquivo especificado j´a exista, seu conte´udo ´e reescrito. A fun¸c˜ao appendFile, ao inv´es de reescrever o conte´udo do arquivo, simplesmente grava no final do mesmo o string passado como argumento. Finalmente, a fun¸c˜ao readFile lˆe o conte´udo do arquivo, retornando-o como um string. O programa a seguir efetua a c´opia do conte´udo de um arquivo para outro arquivo, ilustrando o uso das fun¸c˜oes acima:
O exemplo a seguir ilustra a implementa¸c˜ao de um programa que lˆe de um arquivo uma lista de compras de supermercado e imprime a nota de compras correspondente, com o pre¸co de cada produto e o valor total da compra. Consideramos os seguintes tipos:
type Cents = Int type PriceList =[(String,Cents)] type ShoppingList=[(String,Cents)]
Supomos que a lista de pre¸cos dos produtos do supermercado est´a armazenada em um arquivo, cujo conte´udo tem o formato exemplificado a seguir:
[ ("Caf´e",230), ("Arroz",576), ("Suco",320), .. . ("P~ao de Forma",120)]
A seguinte fun¸c˜ao obt´em o conte´udo deste arquivo:
readPriceList :: File -> IO PriceList readPriceList fname = do contents <- readFile fname return (read contents)
A fun¸c˜ao que define a intera¸c˜ao do programa com o usu´ario consiste em um loop, que aguarda a entrada de um item e a quantidade comprada do mesmo, terminando a intera¸c˜ao quando for digitado um string nulo:
readShoppinList :: IO ShoppingList readShoppinList = do putStr "Digite um item de compra: " item <- getLine if item == then return [] else do putStr "Quantidade = " q <- readLn items <- readShoppinList return ((item,q):items)
Usando essas duas fun¸c˜oes, o programa principal pode ser implementado do seguinte modo:
main :: IO () main = do prices <- readPriceList shlist <- readShoppingList writeBillList "NotaDeCompra" prices shList appendFile "NotaDeCompra" ("Total = " ++ show (costs prices shlist))
Deixamos como exerc´ıcio que vocˆe defina as fun¸c˜oes:
O sistema de entrada e sa´ıda em Haskell inclui um mecanismo simples de tratamento de exce¸c˜oes. Exce¸c˜oes provocadas por opera¸c˜oes de entrada e sa´ıda s˜ao representadas por va- lores do tipo abstrato IOError. O tipo de erro ocorrido em uma opera¸c˜ao de E/S pode ser testado por fun¸c˜oes correspondentes, definidas no m´odulo IO:
isAlreadyExistsError :: IOError -> Bool isDoesNotExistError :: IOError -> Bool isAlreadyInUseError :: IOError -> Bool isFullError :: IOError -> Bool isEOFError :: IOError -> Bool isIllegalOperation :: IOError -> Bool isPermissionError :: IOError -> Bool isUserError :: IOError -> Bool
Exce¸c˜oes s˜ao criadas e capturadas por meio das seguintes fun¸c˜oes:
ioError :: IOError -> IO a catch :: IO a -> (IOError -> IO a) -> IO a
A fun¸c˜ao ioError ocasiona uma exce¸c˜ao. A fun¸c˜ao catch estabelece um tratador que trata qualquer exce¸c˜ao ocorrida na por¸c˜ao de c´odigo protegida pelo catch. O tratador n˜ao ´e seletivo: ele captura todas as exce¸c˜oes ocorridas. A propaga¸c˜ao de uma exce¸c˜ao deve ser feita explicitamente em um tratador, ocasionando-se novamente a exce¸c˜ao que n˜ao se desejar tratar. Por exemplo, na defini¸c˜ao:
f g = catch g (\e -> if IO.isEOFError e then return [] else ioError e)
a fun¸c˜ao f retorna [], caso ocorra uma exce¸c˜ao de fim de arquivo durante a execu¸c˜ao da fun¸c˜ao g, protegida pelo catch, ou propaga a exce¸c˜ao e para um tratador mais externo, caso ela seja algum outro tipo de exce¸c˜ao (diferente de fim de arquivo). Quando uma exce¸c˜ao ´e propagada para fora do programa principal, o sistema de execu¸c˜ao de Haskell imprime o erro correspondente e termina a execu¸c˜ao do programa. Alguns exemplos a seguir ilustram o tratamento de exce¸c˜ao. Como dissemos anteriormente, a fun¸c˜ao readIO :: Read a => String -> IO a retorna o valor de tipo a correspondente ao string passado como primeiro argumento, ocasionando uma exce¸c˜ao (de usu´ario), caso o string
[1] Peyton-Jones, Simon, Haskell 98 Language and Libraries: Revised Report, January 2003, available at www.haskell.org.
[2] Thompson, Simom, Haskell: The craft of functional programming, second edition, Addison- Wesley, 2003.