Posts Categorizados ‘Haskell

10
Mar
09

Labirinto em Haskell – tail recursion

Enquanto estava estudando a linguagem de programação Lua, em um capítulo sobre elementos funcionais da linguagem, me deparei com uma explicação interessante para tail recursion. Nela, o autor do livro (Programming in Lua, 1ª Edição) falava como a linguagem implementa completamente tail recursion(que acho que fica bem traduzido como recursão de cauda). Até aí tudo legal, mas melhora.

Bem, pra demonstrar que implementa e também explicar o conceito, o autor se utiliza de um jogo-exemplo: um labirinto. O jogo constrói o labirinto através de funções que chamam umas às outras, porém utilizando-se de recursão de cauda para manter sempre o uso de memória como se só houvesse uma função. Explicação e o exemplo utilizado podem ser encontrados aqui.

Explicando melhor recursão de cauda: o princípio é que não precisamos que uma função fique esperando o retorno de um resultado de outra para terminar sua execução, como acontece normalmente. Se você faz uma função em C, ou um método em Java, que termina com um comando return outraFuncao;, a função original ficaria esperando o termino da chamada para poder terminar sua execução. Só que outraFuncao pode fazer chamadas a outras funções, e essas a mais outras, e assim por diante. Tal carga será deixada para a memória, que acabará por estourar(um stack overflow, ou estouro de pilha). Mas perceba que, no caso de uma instrução final de uma função, não há motivo para a espera de retorno da chamada. Pode-se simplesmente indicar que o retorno será o da função agora chamada, e limpar a memória utilizada pela execução da original. É isto que linguagens funcionais como Haskell e Scheme e linguagens com fortes influências, como Lua, implementam.

A novidade que gostaria de compartilhar aqui é uma implementação do mesmo código feito em Lua, para Haskell. O código pode dar origem a um jogo bastante complexo e infinitamente escalável, já que ao chamar uma nova função (entrar em um novo quarto) nenhuma informação é mantida da que chama, e sua execução é encerrada por completo. Código em Haskell abaixo:

import IO

main = do
         room1

room1 = do
          putStrLn "You're in room #1"
          text <- getLine
          if text=="south" then
            room3 -- movendo-se pro quarto 3
            else
              if text=="east" then
                room2 --movendo-se pro quarto 2
                else
                  putStrLn "Invalid move" >>
                  room1 --permanece no mesmo quarto

room2 = do
          putStrLn "You're in room #2"
          text <- getLine
          if text=="south" then
            room4
            else
              if text=="west" then
                room1
                else
                  putStrLn "Invalid move" >>
                  room2 

room3 = do
          putStrLn "You're in room #3"
          text <- getLine
          if text=="north" then
            room1
            else
              if text=="east" then
                room4
                else
                  putStrLn "Invalid move" >>
                  room3 

room4 = do
          putStrLn "You're in room #4! You won!"

O código é simples, elegante, e correto. É este mesmo comportamento que torna a recursão um ponto tão forte em Haskell: iteração não é necessária à linguagem pois recursividade não apresenta peso adicional algum ao código. Isto nos permite escrever funções recursivas muito mais poderosas do que podemos encontrar em uma linguagem como C ou Java, onde a pilha de execução não suporta tantas chamadas quanto uma tarefa mais complexa possa precisar(ex.: inserção em uma árvore AVL com 100 mil nós). Isso nos permite executar códigos como o clássico exemplo do fatorial:

fact x = if x < 0 then error"Fatoriais sao de numeros positivos!"
                       else fact2 x 1 where
                              fact2 0 acumulador = acumulador
                              fact2 x acumulador = fact2 (x-1) (acumulador*x)

Pode-se rodar este exemplo e calcular o fatorial de 100 mil, o que não é possível com recursão em C. Apesar do peso do alto nível de abstração, vemos aí parte do poder do paradigma funcional e da linguagem Haskell!

PS: Erro corrigido no quanto à recursividade do fatorial. Ah, e o exemplo do fatorial pode não dar Stack Overflow, mas dependendo da entrada você pode atingir picos de uso de CPU. =)

04
Jan
09

Movendo uma imagem usando Haskell e wxHaskell

Há pouco mais de um ano, quando eu estava cursando a cadeira de Linguagens de Programação Funcionais, tive o prazer de começar a utilizar interfaces gráficas em Haskell, linguagem utilizada pela disciplina. Dentre os vários toolkits gráficos disponíveis, o que nos foi apresentado foi o wxHaskell, na verdade um binding para o kit gráfico multi-plataforma/linguagem wxWidgets.

Tivemos vários problemas com o desenvolvimento:

  • o desenvolvimento gráfico para esse binding era exclusivamente textual, logo não há a possibilidade de modelar visualmente(pelo menos que nós conhecêssemos);
  • nossos prazos eram curtos, dado que a parte gráfica só foi nos ensinada no final da disciplina;
  • boa parte do que tentávamos fazer levava um bom tempo para ser estudado e descoberto como ser feito;
  • nossa equipe tinha poucas possibilidades de reunião, se resumindo a através de chat, geralmente;

Mas isso não é o foco deste post. No final das contas, o projeto foi feito.

wxHaskell

wxHaskell


Enquanto produzia, para prototipar o que eu poderia usar do toolkit, comecei a fazer brincadeiras. Uma delas seria fazer algo que poderia se tornar um jogo: movimentar uma imagem na tela. A imagem poderia ser um personagem, em um hipotético jogo. =)

Alguns passos fazem-se necessários. É necessário o GHC instalado, e o a biblioteca wxHaskell. Utilizei-nos no Windows, e tive inclusive que utilizar uma versão desatualizada do GHC para tornar possível a execução. Nunca mais o executei, e futuramente posso colocar uma descrição detalhada de como instalar tudo necessário. =)

Como foi feito? Explicando em alguns passos:
1) Importação dos módulos necessários:

import Graphics.UI.Wx
import Graphics.UI.Wxcore

2) Define-se um jogador, o qual encapsula a imagem a ser impressa e uma posição(um ponto na tela):

data Player = Player FilePath Point String

O tipo FilePath é um apelido para o tipo String, não se diferencia em nada de um pedaço de texto, representando um caminho nos arquivos para algo, que no nosso caso será a imagem. Point é um TAD que encapsula dois números – Integers, se não me engano – e faz parte da biblioteca do wx mesmo. A String final é apenas uma representação do nome do jogador. =)
3) Define-se a função principal, a que executará o programa através da interface gráfica. Aqui a chamaremos de gui:

gui = do

esta função é um Monad, ou seja, ela encapsula comportamento imperativo no paradigma funcional da linguagem. A partir da diretiva do o comportamento será similar a código imperativo, onde as chamadas a funções estarão separadas por quebras de linhas, de maneira ordenada. Claro, permanece a necessidade de organização de código através de indentação. Prosseguindo:

gui = do
	jog <- varCreate (Player "testFace.gif" (Point 0 0) "joao")
	j <- frameFixed [
		clientSize := Size 300 200,
		text := "teste jogo",
		picture :=	"face.ico",
		on paint := (\dc rect -> do
			x <- varGet jog
			printPlayer x dc
			return ()
			)
	]

Temos no código acima a criação de uma variável, jog, a qual representará um jogador(o tipo abstrato Player, mostrado acima). Passamos na criação a String “testFace.gif”, que é o nome do arquivo que coloquei na mesma pasta que o programa. O ponto passado como ponto inicial foi (Point 0 0), o ponto mínimo de exibição.

testeFace.gif!

testeFace.gif!


Na linha seguinte, definimos j, o qual será um Frame de tamanho fixo, com o uso da função frameFixed, onde passamos a lista de argumentos. Widgets em wxHaskell seguem geralmente esta forma, uma função relacionada que recebe uma lista de argumentos, onde outros podem ser configurados posteriormente. No caso, definimos o tamanho utilizável da janela em clientSize(com o TAD Size, que funciona recebendo dimensões de largura e altura), o texto da barra da janela em text, o ícone da janela em picture, a função que será chamada no momento da “pintura” do widget na tela, em on paint. A função, que declaramos on the fly através de uma expressão lambda, recebe um contexto de dispositivo(dc – device context), que neste caso é a janela, e um retângulo(rect), que especifica as coordenadas da área de visualização. Passaremos o dc como segundo argumento para a função de impressão, printPlayer. O primeiro argumento que passamos é o Player da variável jog, através da constante x, inicializada em:

x <- varGet jog 

A função printPlayer pode ser descrita no código como:

printPlayer :: Player->DC a->IO ()
printPlayer (Player a b c) dc = do
	im <- bitmapCreateFromFile a
	drawBitmap dc im b True []

ou seja, recebe o Player e o Device Context, e utiliza a função de criação de bitmap a partir de um caminho, bitmapCreateFromFile, e a função de desenho de bitmaps, drawBitmap, ambas últimas da própria API de WxHaskell. Temos a criação do bitmap im, que é impresso no dispositivo dc, nas cordenadas indicadas pelo ponto b, com o modo de transparência especificado(True). A lista vazia que está como último argumento são as propriedades, as quais não cheguei a utilizar(e descobrir utilidade).
Continuando no código da função gui, após a criação do FrameFixed j, configuramos alguns eventos para ele. Acredito que seja um padrão de codificação configurar os eventos fora da lista principal de atributos. Escolhi só deixar o on paint por ter caráter intrínsico ao frame, diferente dos que criaremos agora:

	set j [
		on downKey := (do
			varUpdate jog desce
			repaint j
			return ();
			),
		on upKey := (do
			varUpdate jog sobe
			repaint j
			return ();
			),
		on rightKey := (do
			varUpdate jog direita
			repaint j
			return ();
			),
		on leftKey := (do
			varUpdate jog esquerda
			repaint j
			return ();
			)
		]

os eventos configurados acima servem para movimentar a imagem a partir das setas do teclado. WxHaskell já utiliza eventos para as 4 teclas direcionais, listadas acima(ex: on rightKey, onde rightKey é o evento de apertar a seta para a direita). O que acontece a cada evento: é chamada a função varUpdate, da própria API de WxHaskell, a qual é uma função polimórfica. Se não me engano, sua assinatura é a seguinte:

varUpdate::a->(a->a)->IO ()

ou seja, recebe um argumento do tipo a, e uma função que também recebe um argumento deste tipo e retorna outro do tipo a, e finalmente retorna um IO (). Após a chamada de varUpdate, temos a chamada da função repaint, tomando como argumento o frameFixed j. O que esta função faz é disparar o evento de pintura(paint) do argumento, já descrito acima.
Mas o que as funções passadas como argumento para varUpdate – esquerda, direita, sobe e desce – fazem? Vejamos uma delas:

sobe :: Player->Player
sobe (Player a (Point x y) c) = if y==0 then (Player a (Point x y) c)
				else Player a (Point x (y-k)) c

sobe recebe um argumento do tipo Player e retorna o mesmo tipo. A lógica desta função consiste em checar se a posição do jogador já atingiu o topo da janela(quando y é igual a 0), e caso contrário retornar um Player com o valor de y reduzido em k, onde k é uma constante de movimentação, indicando quantos pixels serão saltados. O espaço percorrido por unidade de tempo – ou seja, por disparamento do evento – definirá a velocidade do personagem, sendo seus valores diretamente proporcionais. No meu caso, utilizei:

k::Int --Constante de movimentação
k = 10

As outras funções seguem lógica idêntica, sendo somente necessário checar por colisão do Player com as bordas de j, por isso não as colocarei por extenso aqui. Portanto, a partir de cada evento configurado (o pressionar das setas direcionais) temos uma atualização configurada para a variável jog, e portanto a imagem se comportará da maneira apropriada. Poderíamos brincar um pouco com isso também, fazendo alterações de imagens a partir de determinadas situações, que poderiam ser cláusulas if-then-else nas funções de atualização. Basta usar a criatividade.
Após isso, basta compilar o código e voilá!, você tem uma imagem se movimentando pela sua tela. =)

Comentários de aperfeiçoamentos e hacks a partir disto são bem-vindos! Breve, quem sabe, coloco aqui algo com GTK2Haskell!

06
Dez
08

Novo livro sobre Haskell

Capa do livro Real World Haskell

Real World Haskell

Há um novo livro disponível sobre Haskell: a O’Reilly acabou de lançar, em novembro, o livro Real World Haskell. Para melhorar, o conteúdo está disponível livremente para leitura!

Até onde olhei (pouco mais do que o índice) parece um livro bom. Em geral acho os livros da O’Reilly muito bons, e tenho alguns deles. Este, como o título indica, tem um aspecto bastante prático, e é interessante como lida com vários pontos da linguagem comumente não vistos ao se explicar o paradigma: programação para banco de dados, redes, GUIs, paralelismo e concorrência(algo que aparentemente está em alta para programação funcional atualmente).

No mais, parece bem interessante para ser lido para iniciantes ou para quem deseje melhorar seus conhecimentos práticos sobre Haskell. Mais um livro para o recesso Dezembro – Janeiro!

Página do livro: http://book.realworldhaskell.org/
Encontrado na página inicial de Haskell.

30
Abr
08

Coisas descobertas essa(s) semana(s)

  1. Eu não sei reparticionar HD’s com GParted. É melhor aprender antes de tentar novamente, sob risco de perder todos meus arquivos.
  2. Após instalar o Fedora 8 (ainda não no meu pc), haviam 737 atualizações a serem feitas, o que já havia me sido profetizado.
  3. O jeito que mais me agrada para ver um código direto é: nl nomeDoArquivo | less. Mais rápido que o Notepad++, dentro do terminal, e numerado. Só falta syntax highlighting.
  4. Sim, liberando 20GB devo já conseguir instalar o Fedora. Resta lamentar as atualizações que demorarão horas a serem baixadas.
  5. O orientador de iniciação científica de minha namorada, não satisfeito em escravizá-la, quer me mandar aos trabalhos forçados também.
  6. O meu orientador não parece se importar com isso.
  7. A caminho de implementar uma simulação de máquina de Turing em Haskell(ironia: máquina de turing usando lamda-cálculo)).
  8. Meu horário de sono fica desregulado muito fácil. Um dia entre 18h e 20h, outro dia entre 4h e 6h30, e outro entre 23h e 2h. E consigo ficar mais pontual assim.
  9. Escrever se aprende escrevendo. E lendo. Por isso talvez eu coloque mais alguma coisa aqui em breve: um certo trabalho de Metodologia Científica que me atormenta.

Isso não é feito para ser entendível completamente por ninguém além de mim.




X-Files

 

Janeiro 2010
S T Q Q S S D
« Out    
 123
45678910
11121314151617
18192021222324
25262728293031

Estatísticas:

  • 4,312 erros de pesquisa

possivelmente perigosos:

  • Nenhuma

Tweet! :>

last.fm

Join the Free Software Foundation!

Support freedom