Continuação do Exemplo Simples – Programa completo
12 Julho, 2008
Resolvi continuar aquele exemplo com que estava brincando neste post. Nele, basicamente criei uma função extenso que recebia uma String com um número de quatro dígitos e retornava este número por extenso. Agora, porque não fazermos um programa completo, ao invés de apenas umas função?
Bom, para isso vamos organizar um pouco as coisas: coloquei a função extenso em um arquivo “Exercicio.hs” e declarei, no topo do documento, o módulo Exercicio:
module Exercicio
where
Depois, criei outro arquivo, “Main.hs”, que possuia o seguinte código:
module Main where
import IO
import Exercicio
main = do
hSetBuffering stdin LineBuffering
putStrLn "Numero"
numero <- getLine
putStrLn("" ++ extenso(numero)++"")
Vamos às explicações.
A primeira linha declara o módulo Main. Para compilar um código em Haskell, é preciso que ele possua obrigatoriamente um módulo Main.
A segunda e a terceira linha contêm imports. IO é um módulo que possui funções de entrada e saída. Exercicio é o módulo que foi criado anteriormente e possui a função extenso.
Na linha seguinte, a função main também é obrigatória na compilação de um programa. É onde a execução inicia-se.
Esta função recebe um bloco do. Este bloco é uma das estruturas mais “misteriosas” do Haskell: Monads. Há muito sobre Monads na Internet, basicamente é um bloco em que as linhas são executadas na ordem, o que é
essencial para programas de entrada e saída. Com isso em mente, vamos em frente.
hSetBuffering stdin LineBuffering indica que vamos trabalhar com a linha de comando.
putStrLn "Numero" imprime “Número” na tela e pula uma linha.
numero <- getLine captura o valor que o usuário digitar.
putStrLn("" ++ extenso(numero)++"") imprime o valor da função extenso(numero).
Teste o módulo Main no interpretador. Se estiver funcionando, pode passar para o próximo passo: a compilação.
Na linha de comando, vá até o diretório em que os dois arquivos estiverem e digite
ghc --make Main.hs -o Extenso.exe
Basicamente, o “Extenso.exe” é o nome do arquivo gerado no final pelo GHC, no Linux, o “.exe” não é necessário.
Depois de compilado, é só executar o Exetenso.exe e se divertir.
Tags: iniciantes, Haskell, exemplo, compilação
Calcular uma integral
10 Julho, 2008
Não sei se eu (finalmente) passei em cálculo, mas enfim…
Estava eu vendo uns programinhas em D passados quando achei um que calculava uma integral definida, então resolvi portá-lo para Haskell.
Primeiro algum embasamento teórico:
Basicamente, uma integral é utilizada para se calcular a área sob uma função (não importando sua forma). Basicamente, a idéia é dividir a função f(x) em vários retângulos de comprimendo dx e altura f(n), onde n é o ponto atual. Vale frisar que é a área até o eixo x.
Observe que quanto menor o valor de dx, maior a precisão.
Neste exemplo, utilizaremos retângulos com base de 0,00001 unidades.
A rotina principal para este cálculo é:
area ini fim fn =
let
alturas = map fn [ini,ini+dx .. fim]
areas = map (dx *) alturas
in
foldl (+) 0 areas
where
dx = 0.00001
Onde ini e fim delimitam o intervalo que se está calculando. fn é a função.
As funções utilizadas para isso foram:
map= recebe uma função e uma lista. Retorna uma nova lista, com os valores da função aplicada a cada elemento da lista original. Por exemplo:map (0-) [1,2,3]aplica a função “0-n”, onde n será cada elemento da lista, retornando uma lista [-1,-2,-3].foldl= recebe uma função, um valor e uma lista. Aplica a função ao valor e ao primeiro elemento da lista, então ao valor resultante e ao segundo elemento, e por aí vai. Por exemplo:foldl (+) 0 [1,2,3]retorna a soma aplicaria (((0+1)+2)+3), retornando 6. Vale lembrar que essa função avalia a lista da esquerda para a direita (sim, existe uma foldr)
Basicamente o que é feito é:
- Crio a lista com todos os valores no intervalo:
[ini,ini+dx .. fim]; - Acho as alturas de cada retângulo, aplicando a função passada à essa lista:
alturas = map fn [ini,ini+dx .. fim]; - Acho a área de cada retângulo, multiplicando cada altura pela base (
dx):areas = map (dx *) alturas; - Somo todas as áreas, começando com 0:
foldl (+) 0 areas.
Um uso disse seria:
main = do
(putStrLn.show) (area 0 1 (\x -> x + 3))
Aqui eu calculo a área aproximada da reta x + 3, de x=0 até x=1.
A sintaxe \x -> x + 3 é chamada expressão lambda, e é utilizada para definir uma função anônima inline, que recebe um argumento x e retorna esse argumento + 3.
Listas e Strings – Exemplo Simples
6 Julho, 2008
Há alguns meses (em Maio, para ser mais exato), recebi uma mensagem de um estudante com um problema interessante para ser resolvido em Haskell. Dizia ele que o programa deveria receber como entrada um número de quatro dígitos e retorná-lo por extenso. Por exemplo, a entrada “0123″ deveria ter como resposta: “cento e vinte e três”. Como estou com tempo sobrando (já fechei quase todas as matérias do semestre), resolvi brincar um pouco com esse problema, o que gerou este post.
Resolvi então fazer uma função extenso, que recebe uma string com quatro caracteres e retorna uma string:
extenso ([x,y,z,w]) = mil(x) ++ cen([x, y,z,w]) ++ dec([y,z,w]) ++ if(z == '1')then "" else uni(w)
A linha acima diz que extenso é a concatenação de mil(x), cen([x, y,z,w]), dec([y,z,w]) e if(z == '1')then "" else uni(w), que basicamente seria a concatenação da parte dos milhares, das centenas, das dezenas e das unidades. O “if” no final é para que a parte das unidades só fosse processada quando o valor das dezenas fosse diferente de ‘1′ (quando a dezena vale 1, a unidade é tratada junto com a dezena).
Vamos ver as outras funções:
mil(x):
mil(x) = (case x of
'0' -> ""
'1' -> "mil"
'2' -> "dois mil"
'3' -> "tres mil"
'4' -> "quatro mil"
'5' -> "cinco mil"
'6' -> "seis mil"
'7' -> "sete mil"
'8' -> "oito mil"
'9' -> "nove mil")
A função acima retorna a parte dos milhares. é bem simples, apenas retorna a parte dos milhares por extenso (e “” para quando o valor dos milhares for zero).
cen([x, y,z,w]):
cen([x, y,z,w]) = (if(not(x=='0') && ((y=='0' && (not(z=='0') || not(w=='0'))) || not(y=='0') && z=='0' && w == '0'))then " e " else "") ++ (case y of
'0' -> ""
'1' -> (if (z == '0' && w == '0') then "cem" else "cento")
'2' -> " duzentos"
'3' -> " trezentos"
'4' -> " quatrocentos"
'5' -> " quinhentos"
'6' -> " seiscentos"
'7' -> " setecentos"
'8' -> " oitocentos"
'9' -> " novecentos") ++ if ((y=='0') || (z == '0' && w == '0')) then "" else " e "
Retorna a parte das centenas. o if inicial e o final tratam o aparecimento do “e” entre as palavras. Repare também no tratamento do ‘1′, que às vezes aparece como “cem” e outras como “cento”.
dec([y,z,w]):
dec([y,z,w]) = (case z of
'0' -> ""
'1' -> decDez(w)
'2' -> "vinte"
'3' -> "trinta"
'4' -> "quarenta"
'5' -> "cinquenta"
'6' -> "sessenta"
'7' -> "setenta"
'8' -> "oitenta"
'9' -> "noventa" ) ++ if(z == '0' || z== '1' || w == '0') then "" else " e "
decDez(w) = case w of
'0' -> "dez"
'1' -> "onze"
'2' -> "doze"
'3' -> "treze"
'4' -> "quatorze"
'5' -> "quinze"
'6' -> "dezesseis"
'7' -> "dezesete"
'8' -> "dezoito"
'9' -> "dezenove"
Aqui tratamos a parte das dezenas. Quando o valor for ‘1′, temos que fazer um tratamento especial, então usamos a função decDez(w). No final temos um if para tratar o uso do “e”.
uni(x):
uni(x) = case x of
'0' -> ""
'1' -> "um"
'2' -> "dois"
'3' -> "tres"
'4' -> "quatro"
'5' -> "cinco"
'6' -> "seis"
'7' -> "sete"
'8' -> "oito"
'9' -> "nove"
Finalmente, a função uni(x) cuida da parte das unidades.
Este problema é de fácil resolução, deixo como exercício para quem quiser brincar um pouco com listas. Bug do meu código: o valor “0000″ retorna “” (faltou tratar isso =P).
Tags: haskell, iniciantes, lista
