Haskell/Estruturas de controle
Este módulo encontra-se em processo de tradução. A sua ajuda é bem vinda. |
Como já vimos, Haskell fornece várias maneiras de expressarmos a uma escolha entre diferentes valores. Neste capítulo faremos uma revisão sobre o que já vimos e aprenderemos sobre expressões case
.
if
e guardas
[editar | editar código-fonte]Já vimos que a sintaxe para condicionais é:
if <condição> then <se verdeiro> else <se falso>
<condição> é uma expressão que deve retornar um Bool. Se <condição> for True
, então <se verdadeiro> será avaliada. Caso contrário, <se falso> será avaliada.
Diferente de outras linguagens, em Haskell, if
são expressões que sempre serão avaliadas e devem sempre retornar algum valor. Em C ou Java, por exemplo, se a condição de if
for falsa e não houver uma cláusula else
, nenhuma ação é executada. Em Haskell, como a expressão sempre deve retornar um valor, a cláusula else
é obrigatória.
Outra implicação disso é que podemos usar expressões if
em qualquer lugar onde cabeira qualquer outra expressão. Por exemplo:
g x y = (if x == 0 then 1 else (sin x)/x) * y
Além disso, também vemos que aninhar expressões if
é quase sempre intercambiável com guardas:
h :: Char -> String
descreverLetra c =
if c >= 'a' && c <= 'z'
then "Letra minúscula"
else if c >= 'A' && c <= 'Z'
then "Letra mainúscula"
else "Não é uma letra ASCII"
descreverLetra' :: Char -> String
descreverLetra' c
| c >= 'a' && c <= 'z' = "Letra minúscula"
| c >= 'A' && c <= 'Z' = "Letra mainúscula"
| otherwise = "Não é uma letra ASCII"
Entretanto, guardas não são expressões. Portanto, elas não podem ser usadas e qualquer lugar. Elas também são avaliadas na ordem em que aparecem, assim com acontecem com casamento de padrões. Considere
f <padrão 1> | <predicado 1> = w
| <predicado 2> = x
f <padrão 2> | <predicado 3> = y
| <predicado 4> = z
Primeiramente, os padrões são casados e depois a guardas são avaliadas:
- Se a condição de <predicado 2> for válida, ela só será avaliada se o <padrão 1> tiver sido casado.
- Se <padrão 2> for casado e <predicado 3> e <predicado 4> forem ambos veradeiros, o resultado será
y
, pois <predicado 3> precede o outro.
Expressões case
[editar | editar código-fonte]Uma outra estrutura de controle que ainda não havíamos citado são as expressões case
.
A seguinte função f
pode ser definida:
f 0 = 18
f 1 = 15
f 2 = 12
f x = 12 - x
De forma equivalente, podemos usar case
:
f x =
case x of
0 -> 18
1 -> 15
2 -> 12
_ -> 12 - x
O mesmo procedimento de casamento de padrões acontece na segunda definição: o argumento x
é casado com algum dentre de case
e descritos à esquerda de ->
. Depois disso, a expressão do lado direito é avaliada.
Indentação é muito importante quando usamos case
Cada caso deve aparecer com uma indentação mais à direita do que a linha que contém a palavra-chave of
, e todos os casos devem ter a mesma indentação. Por exemplo, eis uma outra possível definição de f
:
f x = case x of
0 -> 18
1 -> 15
2 -> 12
_ -> 12 - x
Também podemos vincular variáveis como num caso comum de casamento de padrões:
descreverLista :: [a] -> String
descreverLista ls =
case ls of
(x:xs) -> "O primeiro elemento da lista é " ++ show x ++
", e há outros " ++ show (length xs) ++ " elementos nela."
[] -> "A lista é vazia."
Bem como condicionais if
, mas diferentemente das guardas, podemos usar case
em qualquer lugar onde se aceitaria uma expressão. Por exemplo:
data Cor = Preto | Branco | RGB Int Int Int
descreverCor :: Cor -> String
descreverCor c =
"A cor é "
++ case c of
Preto -> "preta"
Branco -> "branco"
RGB 0 0 0 -> "preto"
RGB 255 255 255 -> "branca"
_ -> "...colorida"
++ ", certo?"
Como vemos, o começo da definição de descreverCor
é um String: "A cor é "
seguido do operador (++)
. Portanto, espera-se que o que vem a seguir seja também um String. Assim, as saídas de todos os casos devem ser também String, para satisfazer as restrições de tipo.
- Exercício
Use case
para escrever a função ifFalso
que possa substituir as expressões if
que já conhecemos.