...collaborate on

Sessão Laboratorial 2


Nesta sessão pretende-se tomar conhecimento com conceitos básicos da linguagem Haskell, nomeadamente:

  • expressões e sua avaliação,
  • tipos básicos,
  • tipos estruturados,
  • utilização do interpretador ghci e do compilador ghc.

ghci: um interpretador da linguagem Haskell

Ao longo deste curso iremos utilizar o interpretador ghci para executar programas escritos na linguagem Haskell. Este interpretador pode ser invocado a partir do Terminal:

$ ghci
   ___         ___ _
  / _ \ /\  /\/ __(_)
 / /_\// /_/ / /  | |      GHC Interactive, version 6.5.20060608, for Haskell 98.
/ /_\\/ __  / /___| |      http://www.haskell.org/ghc/
\____/\/ /_/\____/|_|      Type :? for help.

Loading package base-1.0 ... linking ... done.
Prelude> 

Uma vez no interpretador, podemos realizar uma de duas tarefas:

  1. avaliar expressões
  2. executar um comando do interpretador

Avaliar Expressões: para avaliar uma expressão basta-nos digitar essa expressão no interpretador. Por exemplo:

Prelude> 5 + 3 * 2
11
Prelude> show (3 * 2 + 1)
"7"
Prelude> 2 * sin(pi/2)
2.0
Prelude> "ABC" ++ (reverse "DEF")
"ABCFED"
Prelude> if odd(3) then (1,"IMPAR") else (0,"PAR")
(1,"IMPAR")

Verificamos então que o ghci avalia as expressões introduzidas apresentando o respectivo resultado. Mesmo sem conhecermos ainda todas as funções apresentadas, podemos desde logo observar que o Haskell não se limita a avaliar expressões numéricas --- na realidade, é capaz de processar dados de natureza muito diversa (diferentes tipos de dados) como números inteiros, decimais, valores de verdade (booleanos), caracteres, sequências, etc. Por outro lado verificamos que dispõe já do conhecimento de algumas funções pré-definidas (como sin, odd, etc.). Estas funções (e muitas outras, naturalmente) estão definidas na biblioteca básica da linguagem, a que se dá o nome de Prelude. Daí o prefixo "=Prelude>=" apresentado no ghci: informa-nos que ele (só) tem carregadas as definições dessa biblioteca. (obs.: biblioteca, em programação, consiste num conjunto de definições disponibilizadas para serem (re)utilizadas pelos programadores.)

Execução de Comandos: para além de avaliar expressões, é-nos também possível executar comandos que controlam o comportamento do ghci (como carregar um ficheiro com definições de novas funções, solicitar informação de tipos, etc.). Os comandos mais utilizados serão:

  • :l (fich) - carrega ficheiro (fich),
  • :t (expr) - interroga o interpretador sobre o tipo de (expr),
  • :b (mod) - visualiza o conjunto de definições de (mod),
  • :? - comando de ajuda que fornece uma breve explicação dos comandos disponíveis
  • :q - sai do interpretador

De referir é ainda a integração existente entre o ghci/haskell e o Emacs. De facto, este último dispõe de um modo de edição próprio para a linguagem Haskell, activado automáticamente quando é carregado um ficheiro com extensão .hs, que constitui uma mais valia quando editamos programas nessa linguagem (disponibiliza indentação automática, coloração do texto, etc.).

Tarefa 1 - Verifique qual é o tipo das expressões apresentadas acima.

Tipos básicos e estruturados

O Haskell é uma linguagem fortemente tipada - com isto quer-se dizer que o Haskell força a que todas as expressões da linguagem possuam um tipo.

A linguagem oferece o seguinte conjunto de tipos básicos:

  • Bool - Booleanos: True e False
  • Char - Caracteres: 'a', 'x', 'R', '7', '\n', ...
  • Int - Inteiros de tamanho limitado: 1, -4, 23467, ...
  • Integer - Inteiros de tamanho ilimitado: 36, 37829938847736292938338283
  • Float - Números de vírgula flutuante: 3.5, -45.3, 3e-2, ...
  • Double - Números de vírgula flutuante com precisão dupla
  • () - Unit: ()

Para além dos tipos básicos, o Haskell disponibiliza formas de construir novos tipos a partir de outros existentes.

  • produto cartesiano - (a1, a2) :: (t1, t2) - um par cuja primeira componente a1 é do tipo t1 e a segunda componente a2 é do tipo t2. O produto cartesiano generaliza-se par n-tuplos (i.e. (a1, ..., an) :: (t1, ..., tn),
  • listas - [a1,a2, ..., an] :: [t] - uma lista (sequência) constituída pelos elementos a1, a2, ..., an, todos do tipo t,
  • funções f :: t1 -> t2 - f é uma função que espera um argumento de tipo t1 e cujo resultado é do tipo t2.

Para além dos tipos apresentados, o utilizador pode ainda definir novos tipos (assunto que retomaremos numa próxima sessão).

Tarefa 2:

  • Verifique o tipo das funções utilizadas nas expressões apresentadas acima (e.g. odd, sin, ...)
  • Porque é que o Haskell não aceita as seguintes expressões:
    • 3 + 'A'
    • [1,True]
    • not (0)
    • not not

Tarefa 3: * Teste as definições por compreensão apresentadas na aula teórico prática de Programação Funcional da semana anterior disponível aqui.

Definição de funções

A principa tarefa de um programador na linguagem Haskell consiste na definição de funções que realizem as tarefas pretendidas. As funções são definidas por intermédio de regras de cálculo. A título de exemplo, temos

eZero :: Int -> Bool
eZero x = if x==0 then True else False
Por vezes é conveniente (ou mesmo necessário) separar a definição da função em várias cláusulas. Por exemplo, a mesma função podia ser também ser definida como:
eZero' :: Int -> Bool
eZero' 0 = True
eZero' _ = False

As funções podem ainda possuir mais do que um argumento. Considere:

maior :: Int -> Int -> Int
maior x y = if x>y then x else y
Esta função determina o maior de dois inteiros passados como argumento (e.g. (maior 9 3) resulta em 9).

Note que as definição das funções não deve ser realizada directamente no ghci - devem antes ser definidas num ficheiro (com extensão .hs) que será depois carregado no ghci (comando :l).

Tarefa 4: Defina funções que:

  • dados dois números inteiros retorne um par onde a primeira componente é o menor número e a segunda o maior,
  • dado o comprimento dos dois lados catetos de um triângulo retorne o comprimento da hipotenusa.
  • dado o raio do círculo, retorne o perímetro e a área desse círculo.

Módulos

Um programa em Haskell é formado por um conjunto de módulos. Cada módulo contém um conjunto de definições (declarações de tipos, de funções, de classes, ...) que podem ser utilizados internamente, ou exportados para serem utilizados noutros módulos. Um módulo Haskell é armazenado num ficheiro com extensão .hs, .hs, em que representa o nome do módulo, como declarado na primeira linha do ficheiro. Por exemplo, o ficheiro Teste.hs deverá começar com a declaração seguinte:

module Teste where
   ...

Para utilizar definições contidas num outro módulo é necessário referencia-lo explicitamente. Este tipo de ligação entre módulos estabelece-se utilizando uma declaração import. Como exemplo, vejamos como podemos ter acesso às funções de manipulação de caracteres e strings (listas de caracteres) disponíveis no módulo Char.

module Main where
  import Char
  main = putStrLn ("Isto é o resultado de executar a função "++[toLower x | x <- "MAIN..."])

Uma excepção a esta regra é o módulo Prelude, que constitui a base da linguagem Haskell, e cujas definições estão disponíveis por omissão.

Compilador

Também é possível produzir uma aplicação (ficheiro executável) a partir de um ficheiro em Haskell. Para tal deve-se utilizar o compilador ghc. O módulo a compilar deve conter a função main, que corresponde ao "ponto de entrada" da aplicação (ou seja, essa função será avaliada quando executarmos o programa). A título de de exemplo, para compilar e executar o programa apresentado atrás, devíamos proceder da seguinte forma:

$ ghc -o teste Main.hs
$ ./teste
Isto é o resultado de executar a função main...
$ _

Tarefa 5:

  • Compile o programa apresentado e execute-o.
  • Crie um módulo com as funções definidas atrás (tarefa 3) e modifique o módulo Main para utilizar uma dessas funções.

r7 - 09 Oct 2007 - 12:28:47 - JoseBarros
This site is powered by the TWiki collaboration platformCopyright © by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Ideas, requests, problems regarding TWiki? Send feedback
Syndicate this site RSSATOM