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.

Blogged with the Flock Browser

Tags: , , ,

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.

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

Blogged with the Flock Browser

Tags: , ,