Guardas!
Vamos definir uma função que retorne o nome do mês, dado o seu número.
Seria possível escreve esta função com if
aninhados, assim.
nomeMes m = if m == 1 then "JAN"
else if m == 2 then "FEB"
else if m == 3 then "MAR"
...
else if m == 11 then "NOV"
else "DEZ"
Esta estrutura, contudo, pode ser simplificada com o uso guardas, uma opção que faz sentido quando os parâmetros de uma função podem ser classificados em grupos. Guardas tem a seguinte sintaxe.
nomeFuncao arg1 ... argN
| <condicao1> = <definicao1>
| <condicao2> = <definicao2>
...
| <condicaoM> = <definicaoM>
Especificamente, o exemplo do cálculo do nome dos meses ficaria como a seguir.
nomeMes m
| m == 1 = "JAN"
| m == 2 = "FEV"
| m == 3 = "MAR"
| m == 4 = "ABR"
| m == 5 = "MAI"
| m == 6 = "JUN"
| m == 7 = "JUL"
| m == 8 = "AGO"
| m == 9 = "SET"
| m == 10 = "OUT"
| m == 11 = "NOV"
| m == 12 = "DEZ"
| m == 13 = "ONZ"
Embora no exemplo anterior cada condição seja muito simples, apenas um teste de igualdade, condições podem ser mais complexas, podendo incluir múltiplos testes e computações; a única condição é que resulte em um valor booleano. Por exemplo, vejamos uma função que calcula o maior entre três números.
maiorDeTres a b c
| a >= b && a >= c = a
| b >= c = b
| True = c
Avaliação de cima para baixo
Observe que a avaliação das condições é feita na ordem de suas definições, ou seja, de cima para baixo, e isso é importante porquê alguns parâmetros podem satisfazer mais de uma condição. Vejamos novamente o caso do cálculo de anos bissextos. Nesta definição, se a terceira e segunda guardas fossem invertidas, o ano 1900 seria considerado bissexto, quando na verdade ele não é.
bissexto x
| mod x 4 /= 0 = False -- Não divisíveis por 4
| mod x 400 == 0 = True -- Divisíveis por 4 e por 400
| mod x 100 == 0 = False -- Divisíveis por 4, não por 400, e por 100
| otherwise = True -- Divisíveis por 4, e não por 100
{-
>>>bissexto 5
False
>>>bissexto 200
False
>>>bissexto 2000
True
>>>bissexto 1996
True
>>>bissexto 1900
False
-}
Exercício
Escreva uma função que receba um número representando um mês, um número de 1 a 12, e retorne a quantidade de dias no mês. Assuma que fevereiro sempre tem 28 dias.
Resolução
diasMes m
| m == 1 || m == 3 || m == 5 || m == 7 || m == 8 || m == 10 || m == 12 = 31
| m == 2 = 28
| True = 30
Índice de massa corporal
O índice de massa corporal, IMC, é calculado como o peso dividido pelo quadrado da altura. Um IMC abaixo de 18,5, inclusive, é considerado baixo e acima de 30 é considerado alto; aqueles no intervalo são considerados normais. Defina uma função que, dados peso e altura, decida se o IMC correspondente é Baixo, Normal ou Alto.
Resolução
imc p a
| p / a ^ 2 <= 18.5 = "Baixo"
| p / a ^ 2 <= 25.0 = "Normal"
| p / a ^ 2 <= 30.0 = "Alto"
| True = error "Não sei o que dizer"
otherwise
é verdade
Em alguns dos exemplos acima, a última condição foi simplesmente True
, que cobre todos os outros casos.
Embora correta, esta definição pode parecer estranha. Uma alternativa é usar otherwise
no lugar de True
, com exatamente os mesmos efeitos, como no seguinte exemplo.
maiorDeTres a b c
| a >= b && a >= c = a
| b >= c = b
| otherwise = c
De fato, se usarmos o GHCi para obtermos mais informações sobre otherwise
, veremos que é uma constante, cujo valor é True
.
Isso serve para ilustrar o poder da linguagem, que tem um conjunto reduzido de palavras reservadas e que é estendida usando suas funcionalidades básicas.
Prelude> otherwise
True