Você está na página 1de 86

Programação Funcional

Aula 7

Entrada e Saída

José Romildo Malaquias

Departamento de Computação
Universidade Federal de Ouro Preto

2011.2

1/86
. . . . . .
1 Interação com o mundo

2 Operações de entrada e saída em Haskell

3 Programa em Haskell

4 Combinando operações de entrada e saída

5 Outras operações de entrada e saída

6 Saída bufferizada

7 Expressão do

8 readLn e print

9 Números aleatórios

10 Arquivos

2/86
. . . . . .
Layout

1 Interação com o mundo

2 Operações de entrada e saída em Haskell

3 Programa em Haskell

4 Combinando operações de entrada e saída

5 Outras operações de entrada e saída

6 Saída bufferizada

7 Expressão do

8 readLn e print

9 Números aleatórios

10 Arquivos

3/86
. . . . . .
Programas interativos

▶ Programas interativos podem:


exibir mensagens para o usuário
obter valores informados pelo usuário

▶ De forma geral um programa irá trocar informações com o restante do


sistema computacional:
obter dados do sistema computacional
gravar dados no sistema computacional

▶ Em linguagens imperativas as operações de entrada e saída produzem


efeitos colaterais, refletidos na atualização de variáveis globais que
representam o estado do sistema de computação.

4/86
. . . . . .
Exemplo de programa interativo em C

Programa que obtém dois caracteres digitados pelo usuário e exibi-os em


maiúsculas na tela:
#include <stdio.h>
#include <ctype.h>

int main(void)
{
char x = getchar();
char y = getchar();
printf(”%c%c\n”, toupper(x), toupper(y));
return 0;
}
Supondo que o usuário informe os caracteres ’A’ e ’b’, a execução do
programa produzirá a seguinte interação:
Ab
AB

5/86
. . . . . .
Exemplo de programa interativo em C (cont.)

▶ A aplicação de função getchar() retorna valores diferentes mesmo


quando chamada com os mesmos argumentos (nenhum argumento, neste
caso). A primeira chamada retorna ’A’ e a segunda chamada retorna ’b’.
▶ Isto acontece porque getchar() utiliza uma variável global representando
o dispositivo de entrada padrão. Durante a chamada da função esta variável
é atualizada (efeito colateral), removendo o próximo caracter disponível na
entrada e retornando-o como resultado.
▶ Assim, quando a função getchar() é chamada novamente, o próximo
caracter a ser consumido é o segundo caracter digitado pelo usuário. Esta
informação está na variável global que representa o dispositivo de entrada
padrão.

6/86
. . . . . .
Linguagens puras

▶ Em linguagens puras o valor retornado por uma função depende única e


exclusivamente dos seus argumentos.
▶ Portanto toda vez que uma função é aplicada em um dado argumento, o
resultado é o mesmo.
▶ Assim não é possível implementar uma função que lê um caracter da
mesma maneira que em linguagens impuras, como C.
▶ Exemplo:
let x = getchar ()
y = getchar ()
in …
Em uma linguagem pura os valores de x e y serão iguais, uma vez que são
definidos aplicando a função getchar ao mesmo argumento.

7/86
. . . . . .
O mundo

▶ Para interagir com o usuário, precisamos de uma representação do sistema


de computação onde o programa está sendo executado: o mundo (world).
▶ O mundo é formado por todas as informações no contexto de execução da
alicação, incluindo:
dispositivo de entrada padrão (o teclado)
dispositivo de saída padrão (a tela)
sistema de arquivos (arquivos em disco)
conexões de rede
gerador de números pseudo-aleatórios (usa uma semente que depende do
sistema, como por exemplo o horário atual)

▶ Para representar o mundo usaremos o tipo abstrato World.


▶ Nota: O tipo World não está disponível em Haskell. Ele será usado apenas
para discussão do tema.

8/86
. . . . . .
Modificando o mundo

▶ Em linguagens impuras o mundo (ou parte dele) corresponde a uma


variável global atualizável.
▶ Uma função impura que interage com o mundo pode alterar esta variável,
de foma que uma aplicação posterior da função ao mesmo argumento pode
retornar um valor diferente.
▶ Em uma linguagem pura não há a possibilidade de alterar uma variável.
▶ Uma função pura que interage com o mundo tem um argumento e um
resultado adicionais que representam o mundo antes e o mundo depois da
interação.

9/86
. . . . . .
A função primitiva getChar

getChar :: World -> (Char,World)

getChar é uma função primitiva que:


1 recebe um mundo como argumento,
2 obtém o próximo caracter da entrada padrão (um componente do mundo), e
3 resulta em um par formado
pelo caracter obtido da entrada padrão, e
por um novo mundo, idêntico ao anterior exceto na representação da entrada
padrão, pois um caracter foi extraído.

10/86
. . . . . .
A função primitiva putChar

putChar :: Char -> World -> World

putChar é uma função primitiva que:


1 recebe um caracter e um mundo como argumentos,
2 exibe o caracter no dispositivo de saída padrão (um componente do
mundo), e
3 resulta em um novo mundo, idêntico ao anterior exceto na representação da
saída padrão, pois um caracter foi nela inserido.

11/86
. . . . . .
Exemplo usando getChar e putChar

Programa que obtém dois caracteres digitados pelo usuário e exibi-os em


maiúsculas na tela, usando funções puras:
prog :: World -> World

prog world =
let (c1,world1) = getChar world
(c2,world2) = getChar world1
world3 = putChar (toUpper c1) world2
world4 = putChar (toUpper c2) world3
in world4
prog é uma função que:
1 recebe um mundo como argumento,
2 obtém dois caracteres da entrada padrão (um componente do mundo),
3 exibe-os em maiúsculas na saída padrão (outro componente do mundo), e
4 retorna um novo mundo igual ao primeiro exceto pelos dispositivos de
entrada e saída: dois caracteres foram extraídos da entrada e dois
caracteres foram inseridos na saída.
12/86
. . . . . .
Execução seqüencial

▶ Observe que o mundo resultante de uma operação é passado como


argumento para a próxima operação.
▶ Isto leva a uma execução sequencial das operações que usam o mundo.
▶ Ao ser executado pelo sistema operacional, o programa recebe o mundo do
sistema, realiza as operações especificadas, e devolve o novo mundo para
o sistema operacional.
▶ Quando as operações com o mundo são executadas em uma única
seqüência, a implementação da linguagem pode otimizar o código de forma
que o mundo resultante é produzido por uma atualização no mundo dado
como argumento, ao invés do uso de uma cópia modificada.

13/86
. . . . . .
Exemplo: lendo uma linha

getLine :: World -> (String,World)

getLine world = let (x, world1) = getChar world


in if x == ’\n’
then (””,world1)
else let (xs, world2) = getLine world1
in (x:xs, world2)

14/86
. . . . . .
Exemplo: escrevendo uma string

putStr :: String -> World -> World

putStr [] world = world


putStr (x:xs) world = let world1 = putChar x world
world2 = putStr xs world1
in world2

15/86
. . . . . .
Exemplo: escrevendo uma string (cont.)

putStrLn :: String -> World -> World

putStrLn xs world = let world1 = putStr xs world


world2 = putChar ’\n’ world1
in world2

16/86
. . . . . .
Exemplo: soma de dois números

Desenvolver um programa que solicite ao usuário dois números, leia estes


números, e exiba a sua soma.
main :: World -> World

main world =
let world1 = putStr ”Digite um número: ” world
(str1,world2) = getLine world1
world3 = putStr ”Digite outro número: ” world2
(str2,world4) = getLine world3
world5 = putStrLn (show (read str1 + read str2)) world4
in world5

17/86
. . . . . .
Layout

1 Interação com o mundo

2 Operações de entrada e saída em Haskell

3 Programa em Haskell

4 Combinando operações de entrada e saída

5 Outras operações de entrada e saída

6 Saída bufferizada

7 Expressão do

8 readLn e print

9 Números aleatórios

10 Arquivos

18/86
. . . . . .
O construtor de tipo IO

▶ Em Haskell IO a é o tipo das operações de entrada e saída que


interagem com o mundo e resultam em um valor do tipo a.
▶ IO a é um tipo abstrato, logo sua representação não está disponível nos
programas em Haskell.
▶ Haskell provê:
algumas operações de entrada e saída primitivas, e
um mecanismo para combinar operações de entrada e saída.

19/86
. . . . . .
O construtor de tipo IO (cont.)

▶ Embora o tipo World discutido anteriormente não exista em Haskell,


podemos usá-lo para entendermos o funcionamento dos mecanismos de
entrada e saída.
▶ IO a poderia ser definido como:
type IO a = World -> (a,World)

ou seja, uma operação de entrada e saída poderia ser uma função que
recebe um mundo como argumento e interage com este mundo produzindo
um valor e um novo mundo como resultados.

20/86
. . . . . .
A operação getChar

getChar :: IO Char

▶ getChar é uma operação de E/S primitiva que interage com o mundo


extraindo o próximo caracter disponível na entrada padrão.
▶ O tipo de getChar poderia ser expresso como:
getChar :: World -> (Char,World)

21/86
. . . . . .
A função putChar

putChar :: Char -> IO ()

▶ putChar é uma função primitiva que recebe um caracter e retorna uma


operação de E/S que interage com o mundo inserindo o caracter na saída
padrão.
▶ O tipo de putChar poderia ser expresso como:
putChar :: Char -> World -> ((),World)
pois putChar é uma função que recebe um caracter e um mundo como
argumentos, insere o caracter na saída padrão (um componente do mundo),
e retorna um par formado pela tupla vazia e pelo novo mundo (igual ao
mundo inicial exceto pela saída padrão, onde o caracter foi inserido).
▶ Como putChar x apenas insere x na saída padrão e não há nenhum
valor interessante para ser o resultado da operação de E/S, a tupla vazia ()
é utilizada como resultado.

22/86
. . . . . .
Layout

1 Interação com o mundo

2 Operações de entrada e saída em Haskell

3 Programa em Haskell

4 Combinando operações de entrada e saída

5 Outras operações de entrada e saída

6 Saída bufferizada

7 Expressão do

8 readLn e print

9 Números aleatórios

10 Arquivos

23/86
. . . . . .
Programa em Haskell

▶ Um programa em Haskell é uma coleção de módulos.


▶ Um dos módulos deve ser chamado Main e deve exportar a variável
main , do tipo IO t , para algum t.
▶ Quando o programa é executado, a computação main é realizada, e o seu
resultado (do tipo t) é descartado.

24/86
. . . . . .
Exemplo de programa em Haskell

module Main (main) where

main :: IO ()
main = putChar ’A’

Quando o programa é executado:


1 main recebe (automaticamente) como argumento o mundo existente antes
de sua execução,
2 realiza operações de entrada e saída
3 resultando em uma tupla vazia (nenhum valor interessante é produzido), e
4 produzindo um novo mundo que reflete o efeito das operações de entrada e
saída realizadas.

25/86
. . . . . .
Preparando e executando um programa em Haskell

1 Grave o código fonte do programa em um arquivo texto, digamos


putchar-a.hs
2 Compile o programa (por exemplo usando o Glasgow Haskell Compiler em
um terminal):
$ ghc --make putchar-a
[1 of 1] Compiling Main ( putchar-a.hs, putchar-a.o )
Linking putchar-a ...

3 Execute o programa já compilado:


$ ./putchar-a
A

26/86
. . . . . .
Layout

1 Interação com o mundo

2 Operações de entrada e saída em Haskell

3 Programa em Haskell

4 Combinando operações de entrada e saída

5 Outras operações de entrada e saída

6 Saída bufferizada

7 Expressão do

8 readLn e print

9 Números aleatórios

10 Arquivos

27/86
. . . . . .
Sequenciamento de operações

▶ Sendo IO a um tipo abstrato, como poderíamos combinar duas


operações em sequência?
▶ Exemplo: como exibir os caracteres ’A’ e ’B’ em sequência?
▶ Os operadores binários (>>) e (>>=) permitem combinar duas
operações de entrada e saída a serem executadas em sequência.
▶ Estes operadores tem precedência 1 e associatividade à esquerda.

28/86
. . . . . .
Sequenciamento simples

infixl 1 >>
(>>) :: IO a -> IO b -> IO b

▶ Recebe duas ações como argumentos, e as executa em sequência.


▶ O resultado da primeira ação é ignorado.
▶ O resultado da segunda ação é o resultado final.

29/86
. . . . . .
Sequenciamento simples (cont.)

▶ Exemplo:
Exibir dois caracteres na tela:
main = putChar ’A’ >> putChar ’B’

30/86
. . . . . .
Sequenciamento simples (cont.)

▶ Exemplo:
Ler um caracter e ignorá-lo, e exibir três outros caracteres:
main = getChar >>
putChar ’F’ >>
putChar ’i’ >>
putChar ’m’

31/86
. . . . . .
Sequenciamento simples (cont.)

▶ (>>) poderia ser definido como


p >> q = \world -> let (_, newWorld) = p world
in q newWorld

32/86
. . . . . .
Sequenciamento com passagem de resultado

infixl 1 >>=
(>>=) :: IO a -> (a -> IO b) -> IO b

▶ Recebe uma ação e uma função (que resulta em outra ação) como
argumentos.
▶ Executa a primeira ação.
▶ Em seguida aplica a função ao resultado da primeira ação, o que faz com
que a segunda ação seja executada.
▶ Desta forma o resultado da primeira ação é passado como argumento para
a função dada.

33/86
. . . . . .
Sequenciamento com passagem de resultado (cont.)

▶ Exemplo:
Ler um caracter e exibi-lo na tela:
main = getChar >>= (\x -> putChar x)
ou ainda
main = getChar >>= \x -> putChar x
ou ainda
main = getChar >>= \x ->
putChar x
ou ainda
main = getChar >>= putChar

34/86
. . . . . .
Sequenciamento com passagem de resultado (cont.)

▶ Exemplo:
Ler um caracter e exibi-lo em maiúsculas na tela:
main = getChar >>= (\x -> putChar (toUpper x))
ou ainda
main = getChar >>= (putChar . toUpper)

35/86
. . . . . .
Sequenciamento com passagem de resultado (cont.)

▶ Exemplo:
Ler um caracter e exibi-lo em minúsculas e em maiúsculas na tela:
getChar >>= (\x -> putChar (toLower x) >> putChar (toUpper x))
ou ainda
getChar >>= \x -> putChar (toLower x) >> putChar (toUpper x)
ou ainda
getChar >>= \x ->
putChar (toLower x) >>
putChar (toUpper x)

36/86
. . . . . .
Sequenciamento com passagem de resultado (cont.)

▶ (>>=) poderia ser definido como


p >>= f = \world -> let (x, newWorld) = p world
in (f x) newWorld

37/86
. . . . . .
Sequenciamento com passagem de resultado (cont.)

▶ (>>=) pode ser usado para definir (>>)


p >> q = p >>= \_ -> q

38/86
. . . . . .
Função return

return :: a -> IO a

▶ Às vezes é necessário escrever uma operação de entrada e saída que não


faz nada com o mundo e retorna um valor especificado.
▶ return recebe um valor e retorna uma operação de entrada e saída que
não faz nada com o mundo e resulta no valor recebido.
▶ Exemplo:
acao :: IO Char
acao = return ’A’
▶ Exemplo:
main :: IO ()
main = return ’B’ >>= putChar

39/86
. . . . . .
Função return (cont.)

▶ A função return poderia ser definida como:


return x = \world -> (x,world)

40/86
. . . . . .
Layout

1 Interação com o mundo

2 Operações de entrada e saída em Haskell

3 Programa em Haskell

4 Combinando operações de entrada e saída

5 Outras operações de entrada e saída

6 Saída bufferizada

7 Expressão do

8 readLn e print

9 Números aleatórios

10 Arquivos

41/86
. . . . . .
A operação getLine

getLine :: IO String

▶ Lê uma linha da entrada padrão.


▶ getLine está na biblioteca padrão.
▶ Possível definição:
getLine :: IO String
getLine = getChar >>= \x ->
if x == ’\n’
then ””
else getLine >>= \xs -> return (x:xs)

42/86
. . . . . .
A função putStr

putStr :: String -> IO ()

▶ Exibe uma string na saída padrão.


▶ putStr está na biblioteca padrão.
▶ Possível definição:
putStr [] = return ()
putStr (x:xs) = putChar x >> putStr xs

43/86
. . . . . .
A função putStrLn

putStrLn :: String -> IO ()

▶ Exibe uma string seguida de uma mudança de linha na saída padrão.


▶ putStrLn está na biblioteca padrão.
▶ Possível definição:
putStrLn s = putStr s >> putChar ’\n’

44/86
. . . . . .
Exemplo: soma de dois números

soma.hs
module Main (main) where

main :: IO ()
main = putStr ”Digite um número: ” >>
getLine >>= \s1 ->
putStr ”Digite outro número: ” >>
getLine >>= \s2 ->
putStr ”Soma: ” >>
putStrLn (show (read s1 + read s2))
Execução do programa onde o usuário informa os números 34 e 17:
34
17
Digite um número: Digite outro número: Soma: 51
O que aconteceu de errado?

45/86
. . . . . .
Layout

1 Interação com o mundo

2 Operações de entrada e saída em Haskell

3 Programa em Haskell

4 Combinando operações de entrada e saída

5 Outras operações de entrada e saída

6 Saída bufferizada

7 Expressão do

8 readLn e print

9 Números aleatórios

10 Arquivos

46/86
. . . . . .
Saída bufferizada

▶ A saída para o dispositivo padrão de saída é bufferizada: o sistema


operacional mantém uma área da memória (buffer) onde armazena os
caracteres a serem enviados para o dispositivo de saída.
▶ Geralmente os caracteres enviados para a saída padrão somente são
transferidos para o dispositivo de saída quando o buffer estiver cheio.
▶ Este mecanismo reduz o número de acesso aos dispositivos de saída (que
são muito mais lentos que o processador), melhorando o desempenho da
aplicação.
▶ Por este motivo as mensagens não aparecem imediatamente quando o
programa anterior é executado.

47/86
. . . . . .
Saída bufferizada (cont.)

▶ A função hSetBuffering definida no módulo System.IO da biblioteca


pode ser utilizada para configurar o modo de bufferização de um
dispositivo.
hSetBuffering :: Handle -> BufferMode -> IO ()

onde Handle é um tipo abstrato que representa um dispositivo de entrada e


saída internamente para o Haskell, e BufferMode é o tipo que representa
um modo de bufferização:
data BufferMode = NoBuffering
| LineBuffering
| BlockBuffering (Maybe Int)

48/86
. . . . . .
Saída bufferizada (cont.)

▶ A expressão
hSetBuffering hdl mode

é uma ação que configura o modo de bufferização para o handler hdl.


▶ O módulo System.IO define variáveis que representam alguns dispositivos
padrões:
stdin :: Handle {- entrada padrão -}
stdout :: Handle {- saída padrão -}
stderr :: Handle {- saída de erro padrão -}
▶ Então para corrigir o problema no exemplo dado anteriormente devemos
adicionar a ação
hSetBuffering stdout NoBuffering

no começo da sequência.

49/86
. . . . . .
Saída bufferizada (cont.)

soma2.hs
module Main (main) where

import System.IO ( stdout,


hSetBuffering, BufferMode(NoBuffering) )

main :: IO ()
main = hSetBuffering stdout NoBuffering >>
putStr ”Digite um número: ” >>
getLine >>= \s1 ->
putStr ”Digite outro número: ” >>
getLine >>= \s2 ->
putStr ”Soma: ” >>
putStrLn (show (read s1 + read s2))
Execução do programa onde o usuário informa os números 34 e 17:
$ ./soma2
Digite um número: 34
Digite outro número: 17
Soma: 51
50/86
. . . . . .
Saída bufferizada (cont.)

Exercício 1
Escreva um programa em Haskell que solicita ao usuário uma temperatura na
escala Fahrenheit, lê esta temperatura, converte-a para a escala Celsius, e exibe
o resultado. Use os operadores (>>) e (>>=).
Use a seguinte equação para a conversão:

5
C= × (F − 32)
9

51/86
. . . . . .
Layout

1 Interação com o mundo

2 Operações de entrada e saída em Haskell

3 Programa em Haskell

4 Combinando operações de entrada e saída

5 Outras operações de entrada e saída

6 Saída bufferizada

7 Expressão do

8 readLn e print

9 Números aleatórios

10 Arquivos

52/86
. . . . . .
Expressão do

▶ Tipicamente computações são construídas a partir de longos


encadeamentos de (>>) e (>>=).
▶ Haskell oferece a expressão do, que permite combinar várias computações
a serem executadas em sequência usando uma notação mais conveniente.
▶ Uma expressão do é uma extensão sintática do Haskell e sempre pode ser
reescrita como uma expressão normal usando os operadores de
sequenciamento (>>) e (>>=) e a expressão let.

53/86
. . . . . .
Expressão do (cont.)

▶ Exemplo:
Um programa para ler dois números e exibir a sua soma:
module Main (main) where

import System.IO ( stdout,


hSetBuffering, BufferMode(NoBuffering) )

main :: IO ()
main = do { hSetBuffering stdout NoBuffering;
putStr ”Digite um número: ”;
s1 <- getLine;
putStr ”Digite outro número: ”;
s2 <- getLine;
putStr ”Soma: ”;
putStrLn (show (read s1 + read s2))
}

54/86
. . . . . .
Expressão do (cont.)

▶ A expressão do usa layout da mesma maneira que let e where.


▶ Assim as chaves { e } e os pontos-e-vírgula ; podem ser omitidos, sendo
substituídos por uso de indentação adequada.

55/86
. . . . . .
Expressão do (cont.)

▶ Exemplo:
Um programa para ler dois números e exibir a sua soma:
module Main (main) where

import System.IO ( stdout,


hSetBuffering, BufferMode(NoBuffering) )

main :: IO ()
main = do hSetBuffering stdout NoBuffering
putStr ”Digite um número: ”
s1 <- getLine
putStr ”Digite outro número: ”
s2 <- getLine
putStr ”Soma: ”
putStrLn (show (read s1 + read s2))

56/86
. . . . . .
Expressão do (cont.)

▶ Tradução da expressão do:


do { padrão <- x ; ações }

x >>= \padrão -> do { ações }

do { x ; ações }

x >> do { ações }

do { let declarações ; ações }



let declarações in do { ações }

do { x }

x

57/86
. . . . . .
Expressão do (cont.)

▶ Exemplo:
do putStrLn ”um”
putStrLn ”dois”

putStrLn ”um” >>
putStrLn ”dois”

58/86
. . . . . .
Expressão do (cont.)

▶ Exemplo:
do x <- getLine
putStrLn (”Você digitou: ” ++ x)

getLine >>= \x ->
putStrLn (”Você digitou: ” ++ x)

59/86
. . . . . .
Expressão do (cont.)

▶ Exemplo:
do let f xs = xs ++ xs
putStrLn (f ”abc”)

let f xs = xs ++ xs
in putStrLn (f ”abc”)

60/86
. . . . . .
Layout

1 Interação com o mundo

2 Operações de entrada e saída em Haskell

3 Programa em Haskell

4 Combinando operações de entrada e saída

5 Outras operações de entrada e saída

6 Saída bufferizada

7 Expressão do

8 readLn e print

9 Números aleatórios

10 Arquivos

61/86
. . . . . .
A ação readLn

readLn :: Read a => IO a

▶ A ação readLn lê uma linha da entrada padrão e a converte para um tipo


que seja instância da classe Read.
▶ Possível definição para readLn:
readLn = do str <- getLine
return (read str)

62/86
. . . . . .
A função print

print :: Show a => a -> IO ()

▶ A função print insere um valor na saída padrão, seguido de uma mudança


de linha.
▶ O tipo do valor deve ser instância da classe Show, pois a função show é
utilizada para convertê-lo para string.
▶ Print pode ser assim definida:
print = putStrLn . show

63/86
. . . . . .
Exemplo: peso ideal

Programa que recebe a altura e o sexo de uma pessoa e calcula e mostra o seu
peso ideal, utilizando as fórmulas

sexo peso ideal


masculino 72.7 × h − 58
feminino 62.1 × h − 44.7

onde h é a altura.

64/86
. . . . . .
Exemplo: peso ideal (cont.)

pesoideal.hs
module Main (main) where

import System.IO ( stdout, hSetBuffering, BufferMode(NoBuffering) )

data Sexo = F | M deriving Read

leDado :: Read a => String -> IO a


leDado prompt = do putStr prompt
readLn

main :: IO ()
main = do hSetBuffering stdout NoBuffering
h <- leDado ”Altura: ”
s <- leDado ”Sexo (F/M): ”
putStr ”Peso ideal: ”
let pesoIdeal = case s of
F -> 62.1 * h - 44.7
M -> 72.7 * h - 58
print pesoIdeal
65/86
. . . . . .
Exemplo: média de 3 notas

Faça um programa que receba três notas de um aluno, calcule e mostre a média
aritmética das notas e a situação do aluno, dada pela tabela a seguir.

média das notas situação


menor que 3 reprovado
entre 3 (inclusive) e 7 exame especial
acima de 7 (inclusive) aprovado

66/86
. . . . . .
Exemplo: média de 3 notas (cont.)

media3notas.hs
module Main (main) where

import System.IO ( stdout, hSetBuffering, BufferMode(NoBuffering) )

leDado :: Read a => String -> IO a


leDado prompt = putStr prompt >> readLn

main :: IO ()
main = do hSetBuffering stdout NoBuffering
n1 <- leDado ”Nota 1: ”
n2 <- leDado ”Nota 2: ”
n3 <- leDado ”Nota 3: ”
let media = (n1 + n2 + n3)/3
putStrLn (”Média: ” ++ show media)
if media < 3
then putStrLn ”Reprovado”
else if media < 7
then putStrLn ”Exame especial”
else putStrLn ”Aprovado”
67/86
. . . . . .
Exemplo: raízes da equação do segundo grau

Faça um programa que leia os coeficientes de uma equação do segundo grau e


calcule e mostre suas raízes reais, caso existam.

68/86
. . . . . .
Exemplo: raízes da equação do segundo grau (cont.)

raizes2grau.hs
module Main (main) where

import System.IO ( stdout, hSetBuffering, BufferMode(NoBuffering) )

raizes2grau a b c
| d > 0 = [ (-b + sqrt d)/(2*a), (-b - sqrt d)/(2*a) ]
| d = 0 = [ -b/(2*a) ]
| otherwise = [ ]
where d = b^2 - 4*a*c

prompt mensagem = putStr mensagem >> readLn

calcRaizes2grau =
do putStrLn ”Cálculo das raízes da equação do segundo grau”
putStrLn ”a x^2 + b x + c = 0”
a <- prompt ”Coeficiente a: ”
b <- prompt ”Coeficiente b: ”
c <- prompt ”Coeficiente c: ”
case raizes2grau a b c of
[r1,r2] -> 69/86
. . . . . .
Exemplo: raízes da equação do segundo grau (cont.)

putStrLn (”Raízes: ” ++ show r1 ++ ” e ” ++ show r2)


[r] ->
putStrLn (”Raíz: ” ++ show r)
[] ->
putStrLn ”Não há raízes reais”

main = do hSetBuffering stdout NoBuffering


calcRaizes2grau

70/86
. . . . . .
Layout

1 Interação com o mundo

2 Operações de entrada e saída em Haskell

3 Programa em Haskell

4 Combinando operações de entrada e saída

5 Outras operações de entrada e saída

6 Saída bufferizada

7 Expressão do

8 readLn e print

9 Números aleatórios

10 Arquivos

71/86
. . . . . .
Números aleatórios

▶ Para gerar um número pseudo-aleatório em Haskell podemos usar o


módulo System.Random.
▶ Atavés da classe Random é possível obter valores aleatórios de uma
variedade de tipos.
▶ A função randomRIO:
randomRIO :: Random a => (a, a) -> IO a

Esta função recebe um par de valores (inf , sup) e retorna uma operação de
entrada e saída que obtém o próximo valor aleatório do tipo a
uniformemente distribuído no intervalo [inf , sup].

72/86
. . . . . .
Números aleatórios (cont.)

Exemplo: Lançamento de dois dados:


lancadados.hs
module Main (main) where

import System.IO (hSetBuffering, stdout, BufferMode(NoBuffering))


import System.Random (randomRIO)

main :: IO ()
main =
do putStrLn ”Lançamento de dois dados”
x <- randomRIO (1,6::Int)
y <- randomRIO (1,6::Int)
putStrLn (”Faces obtidas: ” ++ show x ++ ” e ” ++ show y)

73/86
. . . . . .
Números aleatórios (cont.)

Execução do programa:
$ ./lancadados
Lancamento de dois dados
Faces obtidas: 3 e 5

$ ./lancadados
Lancamento de dois dados
Faces obtidas: 4 e 1

74/86
. . . . . .
Exemplo: adivinhe o número

Escreva um aplicativo que execute adivinhe o número como mostrado a seguir.


▶ Seu programa escolhe o número a ser adivinhado selecionando um inteiro
aleatório no intervalo de 1 a 1.000.
▶ O aplicativo exibe o prompt Adivinhe um número entre 1 e 1000.
▶ O jogador insere uma primeira suposição.
▶ Se o palpite do jogador estiver incorreto, seu programa deve exibir Muito
alto. Tente novamente ou Muito baixo. Tente novamente para ajudar o
jogador a zerar mediante uma resposta correta.
▶ O programa deve solicitar ao usuário o próximo palpite.
▶ Quando o usuário insere a resposta correta, exiba Parabéns, você adivinhou
o número, e permita que o usuário escolha se quer jogar novamente.

75/86
. . . . . .
Exemplo: adivinhe o número (cont.)

adivinha.hs
module Main (main) where

import System.IO ( stdout, hSetBuffering, BufferMode(NoBuffering) )


import System.Random ( randomRIO )

main :: IO ()
main =
do hSetBuffering stdout NoBuffering
putStrLn ”Adivinhe o número v1.0”
putStrLn ”==========================================”
jogar

jogar :: IO ()
jogar =
do numero <- randomRIO (1,1000)
acertar numero
putStrLn ””
deseja <- simOuNao ”Deseja jogar novamente? (s/n) ”
if deseja
then jogar 76/86
. . . . . .
Exemplo: adivinhe o número (cont.)

else return ()

acertar :: Int -> IO ()


acertar numero =
do putStrLn ””
putStr ”Adivinhe um número entre 1 e 1000: ”
s <- getLine
let x = read s
if x == numero
then putStrLn ”Parabéns, você adivinhou o número”
else do if x < numero
then putStrLn ”Muito baixo. Tente novamente”
else putStrLn ”Muito alto. Tente novamente”
acertar numero

simOuNao :: String -> IO Bool


simOuNao prompt =
do putStr prompt
s <- getLine
case s of
[x] | x == ’s’ || x == ’S’ -> return True 77/86
. . . . . .
Exemplo: adivinhe o número (cont.)

| x == ’n’ || x == ’N’ -> return False


_ -> simOuNao prompt

78/86
. . . . . .
Layout

1 Interação com o mundo

2 Operações de entrada e saída em Haskell

3 Programa em Haskell

4 Combinando operações de entrada e saída

5 Outras operações de entrada e saída

6 Saída bufferizada

7 Expressão do

8 readLn e print

9 Números aleatórios

10 Arquivos

79/86
. . . . . .
Arquivos

▶ Haskell possui várias definições para lidar com arquivos.


type FilePath = String

Tipo usado para representar o caminho do arquivo, incluindo o seu nome.


readFile :: FilePath -> IO String

Lê o conteúdo de um arquivo como uma única string.


writeFile :: FilePath -> String -> IO ()

Escreve uma string em um arquivo.


appendFile :: FilePath -> String -> IO ()

Acrescenta uma string no final de um arquivo.

80/86
. . . . . .
Exemplo: processar notas em arquivo

Criar um programa para ler de um arquivo os dados dos alunos de uma turma – o
código, o nome, a nota na primeira avaliação, a nota na segunda avaliação –
calcular a média aritmética das notas e determinar a situação de cada aluno,
gravando os resultados em outro arquivo.

81/86
. . . . . .
Exemplo: processar notas em arquivo (cont.)

Arquivo de entrada:
1234 Pedro 1.5 1.7
1111 Carla 6.2 7.0
2121 Rafael 8.1 8.8
4321 Ivan 5.0 5.2

82/86
. . . . . .
Exemplo: processar notas em arquivo (cont.)

module Main (main) where

import System.IO (hSetBuffering, stdout, BufferMode(NoBuffering))

leNotas arquivo =
do s <- readFile arquivo
return (map (f . words) (lines s))
where
f [cod,nom,n1,n2] = (cod,nom,read n1,read n2)

processaNotas notas =
map f notas
where
f (cod,nom,n1,n2) = (cod,nom,n1,n2,med,sit)
where
med = (n1 + n2)/2
sit | med < 3 = ”reprovado”
| med < 7 = ”exame especial”
| otherwise = ”aprovado”

escreveResultado arquivo resultados = 83/86


. . . . . .
Exemplo: processar notas em arquivo (cont.)

writeFile arquivo (unlines (map f resultados))


where
f (cod,nom,n1,n2,med,sit) = cod ++ ”\t” ++
nom ++ ”\t” ++
show n1 ++ ”\t” ++
show n2 ++ ”\t” ++
show med ++ ”\t” ++
sit

main =
do hSetBuffering stdout NoBuffering
arq1 <- putStr ”Arquivo de entrada: ” >> getLine
arq2 <- putStr ”Arquivo de saída: ” >> getLine
notas <- leNotas arq1
let res = processaNotas notas
escreveResultado arq2 res

84/86
. . . . . .
Exemplo: processar notas em arquivo (cont.)

Arquivo de saída:
1234 Pedro 1.5 1.7 1.6 reprovado
1111 Carla 6.2 7.0 6.6 exame especial
2121 Rafael 8.1 8.8 8.45 aprovado
4321 Ivan 5.0 5.2 5.1 exame especial

85/86
. . . . . .
Fim

86/86
. . . . . .

Você também pode gostar