Haskell/Verdadeiro ou falso
Este módulo encontra-se em processo de tradução. A sua ajuda é bem vinda. |
Igualdade e outras comparaçãoes
[editar | editar código-fonte]No capítulo usamos sinais de igualdade para definir variáveis e funções em Haskell desta forma:
r = 5
Isso significa que durante a avaliação do programa, todas as ocorrências de r
são substituídas por 5
dentro do mesmo escopo. De modo similar, ao avaliar
f x = x + 3
todas os ocorrências de f
seguidas de um número (o argumento de f
), são substituídas por tal valor mais três.
Na Matemática, o sinal de igualdade é usado de forma diferente. Considere o seguinte problema:
Exercícios |
---|
Resolva o seguinte problema: . |
Neste caso, o problema não representar como sendo ou vice-versa. Na verdade, temos uma proposição de que um número , quando somado a 3, resulta em 5. Resolver a equação significa achar qual valor de , se é que existe um, torna a proposição verdadeira. Neste exemplo simples, uma simples manipulação algébrica nos mostra que , isto é, é o número que satisfaz a proposição, pois .
Comparar valores para verificar se são iguais é algo bastante útil para a programação. Na verdade, é algum bastante elementar e necessário. Em Haskell, tais testes se parecem bastante com um equação. Entretanto, já que o sinal de igual já está sendo usado para definir alguma coisa, Haskell usa um sinal duplo de igualdade, ==
, para fazer comparações. Veja:
Prelude> 2 + 3 == 5 True
Aqui, GHCi retorna True
(verdadeiro, em inglês) porque é igual a . E se a equação não for verdadeira?
Prelude> 7 + 3 == 5 False
O resultado é False
(falso, em inglês). Agora vamos usar a função f
definimos no começo deste capítulo:
Prelude> let f x = x + 3 Prelude> f 2 == 5 True
Como esperado. Já que f 2
é avaliado como sendo 2 + 3
, que resulta em 5
, só poderíamos esperar que 5 == 5
retornasse True
.
Nós também podemos comparar dois valores numéricos para saber qual é maior. Haskell possui vários operadores para esses testes: <
para menor que; >
, maior que; <=
, menor que ou igual a; e >=
, maior que ou igual a. Esses testes funcionam exatamente como ==
, igual a. Por exemplo, poderíamos usar <
juntamente com a função area
do capítulo anterior para saber se um círculo com um certo raio tem área menor que um outro valor.
Prelude> let area r = pi * r ^ 2 Prelude> area 5 < 50 False
Valores booleanos
[editar | editar código-fonte]O que acontece quando GHCi tem que determinar se essas comparações são verdadeiras ou falsas? Considere um caso diferente. Se entrarmos com uma expressão aritmética no GHCi, ela é avaliada, e o resultado numérico aparece na tela:
Prelude> 2 + 2 4
Se substituirmos, na comparação, o valor final da expressão aritmética, algo parecido acontece:
Prelude> 2 == 2 True
Enquanto o "4" retornado primeiro representa a contagem de alguma coisa, o "Verdadeiro" é um valor que representa a verdade de uma certa proposição. Tais valores são conhecidos como booleanos.[nota 1] Naturalmente, apenas dois valores são possíveis, os quais já nos foram apresentados: True
e False
.
Introdução a tipos de dados
[editar | editar código-fonte]True
e False
são valores reais, não apenas uma analogia. Em Haskell, valores booleanos tem o mesmo status que valores numéricos, e podemos manipulá-los de formas semelhantes. Um exemplo trivial:
Prelude> True == True True Prelude> True == False False
True
é, de fato, igual a True
, and True
não é igual a False
. Agora responda: é possível dizer se 2
é igual a True
?
Prelude> 2 == True <interactive>:1:0: No instance for (Num Bool) arising from the literal ‘2’ at <interactive>:1:0 Possible fix: add an instance declaration for (Num Bool) In the first argument of ‘(==)’, namely ‘2’ In the expression: 2 == True In an equation for ‘it’: it = 2 == True
Temos um erro no compilador. Na verdade, a pergunta original sequer faze sentido. Não se pode comparar um número com algo que não é um número, ou um booleano com algo não-booleano. Haskell possui essa noção incorporada em si, e a mensagem de erro acima, apesar de longa e um pouco intimidadora, diz exatamente isso: há um número (Num
) do lado esquerdo de ==
, então esperava-se um número do lado direito também; contudo, um booleano (Bool
) não é um número, então o teste de igualdade falha.
Fica claro, então, que valores são separados em tipo, e que esses tipos definem os limites do que podemos ou não fazer com tais valores. True
e False
são valores do tipo Bool
. Já o 2
é um caso um pouco mais complicado, porque existem vários tipos de números na computação, bem como na matemática (apesar de não serem equivalentes). Mas no fim, ainda é um dado Num
. De forma geral, essa característica de limitação é de grande valor, porque podemos usar isso a nosso favor para controlar o comportamento dos nossos programas, criando regras que impedem o uso do programa com tipos que não fazem sentido, o que garante a funcionamento do correto do que desenvolver. Voltaremos a este tópico sobre tipos mais tarde, pois eles são uma parte importante da linguagem Haskell.
Operadores infixos
[editar | editar código-fonte]Um teste de igualdade, como 2 == 2
, também é uma expressão, bem como uma operação aritmética também o é, como 2 + 2
. Ambos os casos são avaliados basicamente da mesma maneira. Quando digitamos 2 == 2
numa sessão do GHCi, ele "responde" com True
, pois simplesmente avaliou a expressão. Na verdade, o operador ==
é uma função de dois argumentos (o lado direito e o lado esquerdo da igualdade). Só que seu uso é diferente: Haskell permite que funções de dois argumentos sejam escritas como operadores infixos, ou seja, que sejam escritas entre seus dois argumentos. Quando o nome da função é, na verdade, caracteres não-alfanuméricos (==
, por exemplo), usá-las como operadores infixos é a forma mais comum. Se você deseja usá-las da forma padrão, ou seja, como operadores prefixos (com o nome da função antes dos argumentos), elas devem ser cercadas por parênteses. Veja:
Prelude> 4 + 9 == 13 True Prelude> (==) (4 + 9) 13 True
É possível converter um booleano em outro também, usando negação. not
é a função de negação: converte True
para False
, e False
para True
.
Prelude> not (5 * 2 == 10) False
Haskell já possui o operador de diferença: /=
, não é igual a. Entretanto, poderíamos definí-lo facilmente usando not
e ==
:
x /= y = not (x == y)
Note que podemos usar a notação de operadores infixos até mesmo na hora de definí-los. Além disso, um operador pode ser definido a partir de qualquer símbolo ASCII.
Outras operações booleanas
[editar | editar código-fonte]Existem outras operações com booleanos bastante úteis que necessárias: ou e e.
A operação A ou B resulta em verdadeiro se A, ou B, ou ambos forem verdadeiros. Ela é definida como sendo o operador (||)
em Haskell:
Prelude> True || True True Prelude> True || False True Prelude> False || True True Prelude> False || False False
A operação A e B resulta em verdadeiro se A e B forem verdadeiros. Ela é definida com osendo o operador (&&)
:
Prelude> True && True True Prelude> True && False False Prelude> False && True False Prelude> False && False False
Guardas
[editar | editar código-fonte]Os programas escritos em Haskell geralmente usam operadores booleanos numa sintaxe bastante conveniente e abreviada. Quando uma mesma lógica é escrita com uma sintaxe diferente, chamamos isso de açúcar sintático. Quer dizer que o código se torna mais "aprazível ao paladar humano". É bom lembra-se deste termo, pois ele aparece bastante nos materiais sobre Haskell.
Um desses "açúcares", é o que chamamos de guardas, e que usa booleanos para facilitar a implementação de função de maneira bem simples. Primeiro, vamos implementar a função de valor absoluto para números reais, também chamada de função modular ou módulo. Sua definição matemática é que: se um número for positivo, seu módulo é ele mesmo; se um número for negativo, seu módulo é seu oposto, ou 0 menos ele mesmo.
Nesta função, a expressão usada para calcular depende do próprio valor de . Se for verdadeiro, então usamos a primeira expressão; se for falso, a segunda. Para expressar esse processo de decisão em Haskell, usamos guardas, sendo que a função ficaria assim:[nota 2]
absoluto x
| x < 0 = 0 - x
| otherwise = x
É interessante ver que o código acima se parece bastante com a definição matemática. Vamos por partes:
- Começamos com uma definição normal de uma função. Primeiro o nome,
absoluto
, depois definindo a quantidade de argumentos que ela aceita,x
.
- Em vez de usar o sinal
=
e começar a escrever a definição, nós escrevemos duas alternativas de comportamento logo a baixo.[nota 3] São essas alternativas que chamamos de guardas. Deve-se lembrar que a indentação (os espaços em branco antes de|
) não são opcionais, e são usados para mostrar que as guardas estão dentro do escopo da definição deabsoluto
.
- Cada guarda começa com uma barra vertical,
|
. Depois dela, deve-se escrever uma expressão que resulte num booleano, também chamada de condição booleana ou predicado. Ela é seguida pelo resto da definição, que começa a partir de=
. A definição descrita numa guarda só será avaliada se, e somente se o predicato for avaliado comoTrue
.
- O case de
otherwise
é avaliado quando nenhum dos outros casos acima for verdadeiro. Nestes caso, sex
não for menor que zero, então ele só pode ser maior que, ou igual a zero. O predicado da última guarda, portanto, poderia serx >= 0
, mas você geralmente vai verotherwise
como um caso para aceitar todas as outras condições não descritas nas guardas anteriores.
otherwise
. Na verdade, ele é definido simplesmente como sendo otherwise = True
. Isso quer dizer que sempre que otherwise
for avaliado, sempre retornará True
e, portanto, a definição descrita em sua guarda será executada.Perceba que escrevemos 0 - x
em vez de -x
. O que acontece é que -
não é uma função de um argumento que retorna 0 - x
. Na verdade, trata-se de uma abreviação sintática. Mesmo sendo bastante útil, ela geralmente causa conflito quando usada em conjunto com a função (-)
, que o operador da subtração. Faça um teste você mesmo no GHCi: calcule 3 menos -4, sem usar parênteses. É para evitar este tipo de problema que decidimos escrever de forma explícita 0 - x
.
Guardas e where
[editar | editar código-fonte]Usar where
dentro de uma função com guardas é bastante comum. Por exemplo: para saber quantas soluções reais uma equação de segundo grau possui, do tipo , temos que calcular seu discriminante, . Se ele for maior que zero, há duas raízes reais; se for nulo, há uma; se for menor que zero, nenhuma. Uma função numSolsReais
em Haskell para calcular esta quantidade, poderia ser escrita assim:
numSolsReais a b c
| delta > 0 = 2
| delta == 0 = 1
| otherwise = 0
where
delta = b^2 - 4*a*c
Notas
[editar | editar código-fonte]- ↑ Este termo foi escolhido em homenagem ao matemático britânico George Boole.
- ↑ Esta função já está disponível nas bibliotecas padrões de Haskell, chamada de
abs
. Não há necessidade de reimplementá-la. - ↑ Na verdade, poderíamos escrever tudo numa linha só, como
f x | caso 1 | caso 2
, mas é sempre preferível quebrar linhas para facilitar a leitura.