Aula 05: Valores e operadores booleanos

Tópicos

Operadores Lógicos

Python possui 3 operadores chamados lógicos, que fazem operações com valores booleanos (True e False):

Os operadores lógicos and e or são operadores binários (necessitam de dois operandos, ou seja, dois valores para operar). Eles são usados para combinar condições simples (ex: saída de operadores relacionais) ou o resultado de outros operadores lógicos, de modo a criar operações lógicas mais complexas. O operador not ('não/negação' em português) é um operador unário. Seu resultado é sempre o inverso (a negação) do operando.

Sejam exp1 e exp2 expressões resultando em valores booleanos (True ou False), temos então:

As tabelas abaixo mostram os resultados dos três operadores lógicos Python para todas possíveis combinações de seus valores de entrada.

Tabela verdade dos operadores and e or:
ABA and BA or B
FalseFalseFalseFalse
FalseTrueFalseTrue
TrueFalseFalseTrue
TrueTrueTrueTrue
Tabela verdade do operador not:
Anot A
FalseTrue
TrueFalse


A tabela abaixo mostra a precedência dos operadores em Python. Isto significa que, em uma expressão, Python avaliará primeiro os operadores de maior nível.

Uma aplicação dos operadores lógicos consiste na simplificação da escrita de comandos condicionais. Veja as seguintes equivalências.

Equivalências:


Exemplo:

Encontrar a variável de maior valor entre três variáveis inteiras a, b e c com valores distintos.

Solução 1: Sem o uso de operadores lógicos

  if a > b:
      if a > c:
          print("a é o maior")
      else:
          print("c é o maior")
  else:
      if b > c:
          print("b é o maior")
      else:
          print("c é o maior")
  

Solução 2: Solução mais compacta com o uso de operadores lógicos

  if a > b and a > c :
      print("a é o maior")
  elif b > c :
      print("b é o maior")
  else:
      print("c é o maior")
  


Na álgebra booleana são válidas as seguintes propriedades/identidades:

Propriedades ComutativasA and B = B and AA or B = B or A
Propriedades DistributivasA and (B or C) = (A and B) or (A and C)A or (B and C) = (A or B) and (A or C)
Propriedades Associativas(A or B) or C = A or (B or C)(A and B) and C = A and (B and C)
Propriedades IdempotentesA and A = AA or A = A
Dupla Negaçãonot not A = A
Elementos AbsorventesA or True = TrueA and False = False
Elementos NeutrosA or False = AA and True = A
Leis de De Morgannot (A or B) = (not A) and (not B)not (A and B) = (not A) or (not B)

Abaixo é apresentado um exemplo de manipulação de uma expressão, usando as propriedades acima listadas, de modo a trocar um operador or por um operador and.

Considere a expressão:

(x < 3) or (y == 0)

Podemos aplicar a dupla negação sem alterar seu resultado:

not not ((x < 3) or (y == 0))

Podemos agora trocar o operador or pelo operador and, aplicando a "Leis de De Morgan":

not (not (x < 3) and not (y == 0))

Finalmente, podemos então simplificar a expressão, trocando os operadores relacionais negados por operadores relacionais complementares:

not (x >= 3 and y != 0)

Curiosidades:

Em linguagem C, o operador lógico and se escreve como && e o operador lógico or como ||.

Em linguagem C, os valores booleanos False e True são definidos como os valores numéricos 0 e 1, respectivamente. Isso significa que o mesmo efeito do operador lógico and pode ser obtido em C através de um simples produto, isto é, A && B pode ser obtido como A * B. Porém, observe que a multiplicação possui maior nível de precedência, o que pode gerar alterações na ordem de execução no caso de expressões mais complexas, consequentemente levando a um resultado diferente do esperado. Já no caso do operador lógico or temos que A || B pode ter o seu efeito reproduzido pela expressão A+B-A*B.

No Python 3, o tipo bool de dados booleanos é uma subclasse dos inteiros. Logo, no Python 3, os valores booleanos False e True se comportam como os valores 0 e 1, respectivamente, em quase todos os contextos, a exceção é que, quando convertidos em uma string, as strings "False" ou "True" são retornadas, respectivamente. Experimente digitar no prompt (>>>) do Python shell os comandos abaixo:

>>> 1 == True
True
>>> 0 == True
False
>>> 0 == False
True
>>> 1 == False
False
>>> str(1)
'1'
>>> str(True)
'True'
>>> str(0)
'0'
>>> str(False)
'False'

Problema 1:

Escreva um programa que determina a data cronologicamente maior entre duas datas fornecidas pelo usuário. Cada data deve ser fornecida por três valores inteiros onde o primeiro representa um dia, o segundo um mês e o terceiro um ano.

Solução 1: Sem o uso de operadores lógicos

# Primeira data.
d1 = int(input("Dia: "))
m1 = int(input("Mês: "))
a1 = int(input("Ano: "))
# Segunda data.
d2 = int(input("Dia: "))
m2 = int(input("Mês: "))
a2 = int(input("Ano: "))

if a1 > a2:
    print("Data1 é maior!")
elif a1 == a2:
    if m1 > m2:
        print("Data1 é maior!")
    elif m1 == m2:
        if d1 > d2:
            print("Data1 é maior!")
        elif d1 == d2:
            print("Datas são iguais!")
        else:
            print("Data2 é maior!")
    else:
        print("Data2 é maior!")
else:
    print("Data2 é maior!")
  

Solução 2: Solução mais compacta com o uso de operadores lógicos

  # Primeira data.
  d1 = int(input("Dia: "))
  m1 = int(input("Mês: "))
  a1 = int(input("Ano: "))
  # Segunda data.
  d2 = int(input("Dia: "))
  m2 = int(input("Mês: "))
  a2 = int(input("Ano: "))

  if a1>a2 or (a1==a2 and m1>m2) or (a1==a2 and m1==m2 and d1>d2):
       print("Data1 é maior!")
  elif a1==a2 and m1==m2 and d1==d2:
       print("Datas são iguais!")
  else:
       print("Data2 é maior!")
  

Solução 3:
Segundo a propriedade distributiva da álgebra booleana temos:

expr1 and (expr2 or expr3) = (expr1 and expr2) or (expr1 and expr3)
Logo, uma outra solução pode ser obtida colocando a condição a1==a2 em evidência:
  # Primeira data.
  d1 = int(input("Dia: "))
  m1 = int(input("Mês: "))
  a1 = int(input("Ano: "))
  # Segunda data.
  d2 = int(input("Dia: "))
  m2 = int(input("Mês: "))
  a2 = int(input("Ano: "))

  if a1>a2 or (a1==a2 and (m1>m2 or (m1==m2 and d1>d2))):
       print("Data1 é maior!")
  elif a1==a2 and m1==m2 and d1==d2:
       print("Datas são iguais!")
  else:
       print("Data2 é maior!")
  

Problema 2:

Dados um número inteiro n>0 e as notas de n alunos, determinar quantos ficaram de recuperação. Um aluno está de recuperação se sua nota final for maior ou igual a 3 e menor do que 5.

Solução 1:

Uma solução usando operadores lógicos:

  n = int(input("Digite n: "))
  rec = 0
  i = 1
  while i <= n:
      nota = float(input("Digite uma nota: "))
      if 3.0 <= nota and nota < 5.0:
          rec = rec + 1
      i = i + 1

  print(rec,"alunos ficaram de recuperação")
  
Do ponto de vista gráfico, usando a notação de conjuntos de intervalos na reta dos reais, temos:

intervalo and

intervalo and

intervalo and

Observe que graficamente o operador lógico "and" possui o efeito de intersecção dos conjuntos.

Solução 2:

Uma segunda solução sem usar operadores lógicos:

  n = int(input("Digite n: "))
  rec = 0
  i = 1
  while i <= n:
      nota = float(input("Digite uma nota: "))
      if nota >= 3.0:
          if nota < 5.0:
              rec = rec + 1
      i = i + 1

  print(rec,"alunos ficaram de recuperação")
  

Solução 3:

O Python ainda permite uma terceira solução, na qual o intervalo numérico é especificado de modo mais similar a nossa linguagem, usando operadores de comparação de forma encadeada:

  n = int(input("Digite n: "))
  rec = 0
  i = 1
  while i <= n:
      nota = float(input("Digite uma nota: "))
      if 3.0 <= nota < 5.0:
          rec = rec + 1
      i = i + 1

  print(rec,"alunos ficaram de recuperação")
  
No entanto, essa versão não é recomendada por não possuir um padrão equivalente em outras linguagens, o que pode gerar erros ao transcrever a solução para outras linguagens. Um exemplo desse erro no caso da linguagem C é explicado no quadro de curiosidades abaixo.

Curiosidades:

Em linguagem C, a construção 30 <= nota < 50 para testar se a nota está no intervalo de recuperação não produz o efeito desejado. No entanto, ela não gera um erro de sintaxe e sim um erro de lógica, sendo, portanto, mais difícil de localizar e corrigir.
Por exemplo, considere o seguinte código em C.
 #include <stdio.h>

 int main(){
   float nota;
   int rec;
   nota = 8.0;
   rec = 3.0 <= nota < 5.0;
   if(rec)
     printf("Ficou de recuperação!\n");

   return 0;
 }
Vamos analisar o comando rec = 3.0 <= nota < 5.0; da sétima linha do código acima. Com exceção do ponto e vírgula presente no final do comando, que faz parte da sintaxe da linguagem C, o resto da linha é praticamente idêntico a um código em Python. Em linguagem C, os operadores relacionais são operadores binários, que operam sobre dois operandos e que produzem como saída 0 ou 1, representando False ou True, respectivamente. Como na sétima linha do código temos dois operadores relacionais (<= e <) que possuem o mesmo nível de precedência, eles são executados da esquerda para a direita.

comparacao de intervalo em C

Portanto, primeiramente executamos o operador "Menor ou igual a" (<=), comparando 3.0 com o valor 8.0 em nota, o que produzirá 1 (verdadeiro). Esse valor resultante 1 será então comparado pelo operador < com o 5.0, produzindo 1 (verdadeiro) como saída. Ou seja, o aluno com nota 8.0 será considerado indevidamente como estando de recuperação, segundo o código acima. Portanto, para testar intervalos em C, precisamos necessariamente do uso do operador lógico "and" (que em C é escrito como &&). Para o exemplo acima, a construção correta em C seria: rec = 3.0 <= nota && nota < 5.0;

Problema 3:

Dados números inteiros n, i e j, todos maiores do que zero, imprimir em ordem crescente os n primeiros naturais que são múltiplos de i ou de j e ou de ambos. Por exemplo, para n=6, i=2 e j=3 a saída deverá ser:
    0    2    3   4    6    8

Primeira solução: Testa os números 0, 1, 2, ... verificando e imprimindo quais são múltiplos de i ou j, até que n múltiplos sejam impressos.
  # dados de entrada
  print("Cálculo dos n primeiros múltiplos de i ou de j")
  n = int(input("Digite n: "))
  i = int(input("Digite i: "))
  j = int(input("Digite j: "))

  cont = 0 #conta quantos múltiplos foram impressos.
  cm = 0 #candidato a múltiplo.
  while cont < n:
      if cm%i == 0 or cm%j == 0:
          print(cm)
          cont += 1
      cm += 1
  
Segunda solução: Mais elaborada. Faz menos iterações que a anterior. A cada iteração imprime um múltiplo de i ou j.
  # dados de entrada
  print("Cálculo dos n primeiros múltiplos de i ou de j")
  n = int(input("Digite n: "))
  i = int(input("Digite i: "))
  j = int(input("Digite j: "))

  multi = 0 # múltiplos de i
  multj = 0 # múltiplos de j
  cont = 0 # conta quantos múltiplo foram impressos

  while cont < n:
      if multi < multj:
          print(multi)
          multi += i
      elif multj < multi:
          print(multj)
          multj += j
      else: # multi == multj
          print(multj)
          multi += i
          multj += j
      cont += 1