Introdução a Haskell

Operadores Matemáticos

A linguagem Haskell possui diversos operadores matemáticos, conforme descritos a seguir em forma de funções:

soma x y       = x + y
subtrai x y    = x - y
multiplica x y = x * y
divide x y     = x / y
divideInt1 x y = x `div` y
divideInt1 x y = x `quot` y
resto1 x y     = x `mod` y
resto2 x y     = x `rem` y
elevado1 x y   = x ^ y
elevado2 x y   = x ** y

A diferença entre div e quot é que o primeiro arredonda o resultado na direção de infinito negativo, enquanto o segundo arredonda na direção de zero. Essa mesma diferença é observada entre mod e rem.

Os operadores de potência diferem quanto ao expoente, o operador ^ recebe apenas expoentes inteiros, enquanto ** aceita expoentes reais.

Além desses operadores, o Haskell também possui as funções log para logaritmo natural, logBase base para logaritmo na base base, sqrt para raíz quadrada, exp para exponencial, abs para valor absoluto. Também possui as funções trigonométricas sin, cos, tan, asin, acos, atan, e funções de arredondamento truncate, round, floor, ceiling.

Notem que a ordem das operações em uma expressão matemática segue uma ordem de precedência na avaliação, considere a seguinte expressão:

A ordem que avaliamos as operações fazem diferença! Se primeiro calcularmos a soma de com e somente após multiplicarmos por resultará em um valor diferente caso resolvamos realizar a multiplicação primeiro. Na matemática, para evitar ambiguidade utilizamos parênteses:

Em linguagem de programação, além do uso de parênteses, a linguagem define a priori a prioridade de cada operador e a ordem de execução. Considere o seguinte algoritmo:

f x = 1 + x * 3
g x = (1 + x) * 3

As funções f e g resultam em valores diferentes, no Haskell a prioridade é definida por um valor numérico de a , em que é a precedência mais alta e a ordem de avaliação: esquerda, direita, indiferente, que determina se a expressão é avaliada da esquerda para direita ou da direita para a esquerda.

No prompt de comando ou terminal execute o ghci para entrarmos no modo interativo, nele digite o comando :info (+), a saída será:

:info (+)
class Num a where
  (+) :: a -> a -> a
  ...
  	-- Defined in ‘GHC.Num’
infixl 6 +

As primeiras linhas indicam que o operador + pode ser aplicado para qualquer valor numérico (Num) e recebe dois valores, retornando um novo valor (a -> a -> a). Ao final, a precedência e associatividade do operador é revelado: infixl, indicando que a expressão é avaliada da esquerda para direita e 6 indicando que a precedência é no nível 6.

Comparando com o operador ^ (:info (^)) temos:

:info (^)
(^) :: (Num a, Integral b) => a -> b -> a 	-- Defined in ‘GHC.Real’
infixr 8 ^

Indicando que a prioridade desse operador é 8 e ele é avaliado da direita para a esquerda. Considere a expressão:

O Haskell irá avaliar a expressão na seguinte sequência:

3 + 4 ^ (5 + 6)
3 + 4 ^ 11
3 + 4194304
4194307

Exercício 01: Calcule a razão áurea dada pela expressão

aurea = (1 + sqrt 5) / 2

Exercício 02: Defina a função para calcular a entropia de um sistema binário dada por , sendo que o logaritmo é na base 2.

entropia p = -p * (logBase 2 p) - (1 - p) * (logBase 2 (1-p))

Nas próximas aulas aprenderemos a tornar essas funções mais simples e concisas.

Exercício 03: Defina uma função para calcular a distância quadrática entre um ponto (definido pelos valores de x e y) e o centro de uma circunferência (definida pelos valores cx e cy), dada pela expressão .

distanciaQuad x y cx cy = (cx - x)^2 + (cy - y)^2

Operadores Lógicos e Relacionais

Considere o seguinte problema: dada a distância quadrática entre um ponto e o centro de uma circunferência, determine se ele está dentro ou fora da circunferência de raio r.

Para resolver tal problema basta verificar se a distância quadrática entre o ponto e o centro é menor ou igual ao quadrado do raio (dentro da circunferência) ou maior (fora).

No Haskell temos os operadores relacionais que verificam a relação entre os valores das expressões:

igual x y      = x == y
diferente x y  = x /= y
maior x y      = x > y
menor x y      = x < y
maiorIgual x y = x >= y
menorIgual x y = x <= y

Esses operadores retorna True (verdadeiro) ou False (falso). Nosso problema poderia ser resolvido como:

estaDentro x y cx cy r = (distanciaQuad x y cx cy) <= r^2

Além disso, o resultado dessas operações podem ser combinadas com operadores lógicos: && (e lógico), || (ou lógico) e not (não lógica), que funcionam da mesma forma que os operadores algébricos:

True && True  == True
True && False == False
True || False == True
False || False == False
not True == False

Exercício 04: determine se um ano é bissexto dado que todo ano bissexto ou é múltiplo de 400 ou é múltiplo de 4 mas não de 100. Verifique a ordem de precedência dos operadores e a necessidade do uso de parênteses.

bissexto ano = (ano `rem` 400 == 0) || ((ano `rem` 4 == 0) && (ano `rem` 100 /= 0))

Na próxima aula aprenderemos sobre os tipos nativos de dados do Haskell, mais alguns operadores e detalharemos o conceito de funções.