Docsity
Docsity

Prepare-se para as provas
Prepare-se para as provas

Estude fácil! Tem muito documento disponível na Docsity


Ganhe pontos para baixar
Ganhe pontos para baixar

Ganhe pontos ajudando outros esrudantes ou compre um plano Premium


Guias e Dicas
Guias e Dicas


Entrada e Saida em Haskell, Notas de estudo de Informática

Entrada e Saida em Haskell

Tipologia: Notas de estudo

2012

Compartilhado em 08/04/2012

alessandro-rezende-12
alessandro-rezende-12 🇧🇷

4.5

(2)

15 documentos

1 / 13

Toggle sidebar

Esta página não é visível na pré-visualização

Não perca as partes importantes!

bg1
Departamento de Computa¸ao
Universidade Federal de Ouro Preto
Programa¸ao Funcional
Prof. Luc´ılia Figueiredo
Entrada e Sa´ıda em Haskell
Tutorial
Elton M. Cardoso
Luc´ılia Figueiredo
30 de Junho de 2005
pf3
pf4
pf5
pf8
pf9
pfa
pfd

Pré-visualização parcial do texto

Baixe Entrada e Saida em Haskell e outras Notas de estudo em PDF para Informática, somente na Docsity!

Departamento de Computa¸c˜ao Universidade Federal de Ouro Preto Programa¸c˜ao Funcional Prof. Luc´ılia Figueiredo

Entrada e Sa´ıda em Haskell

Tutorial

Elton M. Cardoso Luc´ılia Figueiredo

30 de Junho de 2005

Conte´udo

  • 1 E/S em Haskell
    • 1.1 A¸c˜oes e computa¸c˜oes
    • 1.2 Entrada e Sa´ıda Padr˜ao
    • 1.3 Combinando a¸c˜oes
      • 1.3.1 Definindo fun¸c˜oes de intera¸c˜ao
    • 1.4 Lendo e exibindo valores
    • 1.5 Entrada e Sa´ıda em Arquivos
      • 1.5.1 Exemplo
    • 1.6 Tratamento de Exce¸c˜ao

1.2. ENTRADA E SA´IDA PADR AO˜ CAP´ITULO 1. E/S EM HASKELL

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.

1.2 Entrada e Sa´ıda Padr˜ao

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.

1.3 Combinando a¸c˜oes

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.

1.3. COMBINANDO AC¸ OES˜ CAP´ITULO 1. E/S EM HASKELL

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.

1.3.1 Definindo fun¸c˜oes de intera¸c˜ao

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:

1.4. LENDO E EXIBINDO VALORES CAP´ITULO 1. E/S EM HASKELL

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

1.5. ENTRADA E SA´IDA EM ARQUIVOS CAP´ITULO 1. E/S EM HASKELL

main = interact (filter isUpper)

imprime na tela apenas as letras mai´usculas do string lido do teclado.

Exerc´ıcios:

  • Defina uma fun¸c˜ao leIntList que lˆe para uma seq¨uˆencia de valores inteiros do dispositivo de entrada padr˜ao, at´e que seja digitado o valor 0 , e retorna a lista dos valores lidos.
  • Refa¸ca o exerc´ıcio anterior, supondo que os n´umeros devem ser digitados todos em uma ´unica linha.
  • Defina um programa que lˆe uma valor inteiro positivo n e imprime a lista de pares (i, i^2 ), para valores de i no intervalo 1 ≤ i ≤ n.

1.5 Entrada e Sa´ıda em Arquivos

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:

1.5. ENTRADA E SA´IDA EM ARQUIVOS CAP´ITULO 1. E/S EM HASKELL

1.5.1 Exemplo

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:

1.6. TRATAMENTO DE EXCEC¸ AO˜ CAP´ITULO 1. E/S EM HASKELL

  • costs :: PriceList ShoppinList -> Cents, que retorna o valor total da nota de compra, dada a lista a lista de pre¸cos e a lista de compra;
  • writeBillList :: File -> PriceList -> ShoppingLIst -> IO (), que grava a nota de compra no arquivo passado como primeiro argumento, em um formato em que cada linha cont´em o nome de um item, a quantidade comprada e pre¸co total correspondente.

1.6 Tratamento de Exce¸c˜ao

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

Bibliografia

[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.