Aula 06 - Operadores lógicos e precedência de operadores

Introdução

Vamos dar continuidade aos estudos dos operadores em Ruby e, dessa vez, vamos abordar os operadores lógicos. Os operadores lógicos permitem-nos construir expressões complexas usando valores booleanos, e o retorno (resultado) é sempre um valor booleano. A linguagem Ruby possui sete desses operadores. Mas antes de começarmos a tratar sobre eles, vamos a alguns exemplos para nos recordar sobre a sua utilidade.

Um dos operadores lógicos é o and, que significa "e" em inglês. O operador lógico and retorna verdadeiro (true) apenas quando as duas proposições são verdadeiras. Em todos os outros casos, esse operador irá retornar falso (false). Para exemplificar o uso desse operador, considere o exemplo abaixo.

  
    irb(main):001:0> a = true
    => true
    irb(main):002:0> b = true
    => true
    irb(main):003:0> c = false
    => false
    irb(main):004:0> a and b
    => true
    irb(main):005:0> a and c
    => false
    irb(main):006:0> b and c
    => false
  

Observe que o operador and retornou verdadeiro (true) quando as duas variáveis eram verdadeiras (true). Quando pelo menos uma das variáveis era falsa (false), o retorno foi falso (false).

Operadores Lógicos em Ruby

Ruby possui sete operadores lógicos, ilustrados na Tabela 1, que representam quatro operações lógicas.

Tabela 1 - Operadores lógicos
Operação lógica Operador Descrição
E and Resulta verdadeiro quando os dois operandos forem verdadeiros, resulta falso caso contrário
&&
Ou or Resulta verdadeiro quando pelo menos um dos dois operandos for verdadeiro, resulta falso caso contrário
||
Ou exclusivo ^ Resulta verdadeiro quando apenas um operando for verdadeiro. Quando os dois forem verdadeiros, retorna falso
Negação ! Inverte o valor lógico do operando
not

A Tabela 1 acima apresenta todos os operadores lógicos disponíveis em Ruby. Observe que alguns deles representam a mesma operação lógica, como o and e o &&, o || e o or, o ! e o not.

Operação lógica: AND

A operação lógica and irá retornar verdadeiro apenas quando os dois operandos forem verdadeiros. Observe a tabela da verdade do operador and na Tabela 2.

Tabela 2 - Tabela da verdade do operador AND
A B A and B
Verdadeiro Verdadeiro Verdadeiro
Verdadeiro Falso Falso
Falso Verdadeiro Falso
Falso Falso Falso

A operação lógica and pode ser executada utilizando um dos dois operadores lógicos: and ou &&. Observe alguns exemplos de uso desse operador no Exemplo de código 1.

  
    puts "true and true = #{true and true}"
    puts "true && false = #{true && false}"
    puts "false and true = #{false and true}"
    puts "false && false = #{false and false}"
  

Exemplos de uso do operador lógico and

Operação lógica: OR

A operação lógica or irá retornar verdadeiro quando pelo menos um dos operandos for verdadeiro. Observe a tabela da verdade do operador or na Tabela 3.

Tabela 3 - Tabela da verdade do operador OR
A B A or B
Verdadeiro Verdadeiro Verdadeiro
Verdadeiro Falso Verdadeiro
Falso Verdadeiro Verdadeiro
Falso Falso Falso

A operação lógica or pode ser executada utilizando um dos dois operadores lógicos: or ou ||. Observe alguns exemplos de uso desse operador no Exemplo de código 2.

  
    puts "true or true = #{true or true}"
    puts "true || false = #{true || false}"
    puts "false or true = #{false or true}"
    puts "false || false = #{false || false}"
  

Exemplos de uso do operador lógico or

Operação lógica: Ou exclusivo

A operação lógica ‘ou exclusivo’, também conhecida como xor (do inglês exclusive or), retorna verdadeiro quando apenas um dos operadores é verdadeiro. Em todos os outros casos, será retornado falso. Observe a tabela da verdade do ‘ou exclusivo’ na Tabela 4.

Tabela 4 - Tabela da verdade do operador ^
A B A ^ B
Verdadeiro Verdadeiro Falso
Verdadeiro Falso Verdadeiro
Falso Verdadeiro Verdadeiro
Falso Falso Falso

A operação lógica xor pode ser executada utilizando o operador ^. Observe os exemplos de uso desse operador no Exemplo de código 3.

  
    puts "true ^ true = #{true ^ true}"
    puts "true ^ false = #{true ^ false}"
    puts "false ^ true = #{false ^ true}"
    puts "false ^ false = #{false ^ false}"
  

Exemplos de uso do operador lógico ^

Operação lógica: Negação

A operação lógica de negação inverte o valor lógico do operando. Se o operando for verdadeiro ele se tornará falso e se for falso se tornará verdadeiro. Observe a tabela da verdade do operador not no quadro abaixo.

Tabela 5 - Tabela da verdade do operador not
A not A
Verdadeiro Falso
Falso Verdadeiro

A operação lógica not pode ser executada utilizando um dos seguintes operadores: not ou !. Observe os exemplos de uso desse operador no Exemplo de código 4.

  
    puts "not true = #{not true}"
    puts "! false = #{! false}"
  

Exemplos de uso do operador lógico not

Atividade 6.1

Crie um script em Ruby com o código apresentado nos Exemplos de código 1, 2, 3 e 4. Execute o script e veja o resultado apresentado.

Expressões lógicas

Utilizando operadores lógicos e relacionais, nós somos capazes de construir expressões lógicas mais complexas. Confira os exemplos no Exemplo de código 5.

  
    a = true
    b = false
    c = true
    d = false

    exemplo1 = (a and b) or (b and c)
    exemplo2 = (d or c) and not a
    exemplo3 = (a or b) or !c
    exemplo4 = (3 < 4) or (a ^ c)
    exemplo5 = (10 >= 10) and (a or b) or not d

    puts "(a and b) or (b and c) = #{exemplo1}"
    puts "(d or c) and not a = #{exemplo2}"
    puts "(a or b) or !c = #{exemplo3}"
    puts "(3 < 4) or (a ^ c) = #{exemplo4}"
    puts "(10 >= 10) and (a or b) or not d = #{exemplo5}"
  

Exemplos com operadores lógicos e relacionais

Atividade 6.2

Antes de executar o Exemplo de código 5, tente determinar o resultado de cada expressão lógica (linhas de 6 a 10). Depois, execute o código e confira se você acertou.

Se você tentou determinar o resultado de cada expressão lógica da Atividade 6.2, você, provavelmente, deve ter achado algo estranho na expressão da linha 7 e talvez não saiba explicar o porquê. A expressão é a seguinte: (d or c) and not a. Considerando que d é false e c é true, o resultado de (d or c) é true, e, considerando que a é true, not a resulta em false. Por fim, temos true and false que resulta em false. Contudo, na linha 13, o valor da variável exemplo2, que guarda o resultado da expressão da linha 7, é true! Como isso é possível?

Para complicar mais, se você teve a curiosidade de tentar executar a expressão da linha 7 no IRB, vai ver que o resultado da expressão é, de fato, false. Observe o exemplo abaixo.

  
    irb(main):001:0> a = true
    => true
    irb(main):002:0> b = false
    => false
    irb(main):003:0> c = true
    => true
    irb(main):004:0> d = false
    => false
    irb(main):005:0> (d or c) and not a
    => false
    irb(main):006:0> exemplo2 = (d or c) and not a
    => false
    irb(main):007:0> exemplo2
    => true
    irb(main):008:0>
  

No exemplo anterior, definimos os valores das variáveis a, b, c e d, conforme o Exemplo de código 5 e, em seguida, executamos a expressão (d or c) and not a, que resultou em false. Quando atribuímos o resultado dessa mesma expressão para a variável exemplo2, o valor dela é true. Como é possível que o resultado da expressão seja false, mas o valor atribuído à variável seja true? A resposta para essa pergunta está na precedência de operadores.

Precedência de Operadores

Para explicar a precedência de operadores, vamos usar um exemplo da matemática. Qual é o resultado da expressão aritmética abaixo?

3 * 4 + 2 = ?

Se você respondeu que o resultado é 14, parabéns! Você acertou! E se você acertou, você levou em consideração a precedência de operadores. Mas o que é isso? A precedência de operadores determina a ordem de execução dos operadores. Ou seja, quais operações serão executadas primeiro e quais serão executadas depois. Nesse exemplo, sabemos que o operador de multiplicação tem precedência sobre o operador de soma, portanto devemos primeiro multiplicar e depois somar.

Assim como a matemática, as linguagens de programação também possuem precedência de operadores. A Tabela 6 apresenta operadores em ordem de precedência, ou seja, os operadores que aparecem no topo do quadro têm precedência (ou seja, devem ser executados primeiro) sobre os operadores que aparecem da parte de baixo no quadro subsequentes a eles.

Tabela 6 - Precedência em Ruby
Operador
!, ~, + unário
**
- unário
*, /, %
+, -
<<, >>
&
|, ^
>, >=, <, <=
<=>, ==, ===, !=, =~, !~
&&
||
.., ...
?, :
rescue
=, +=, -=, etc.
defined?
not
or, and
if, unless, while, until
blocos { }

Vale salientar que, assim como na matemática, o uso de parênteses garante a precedência de execução da expressão que está entre os parênteses. Um exemplo clássico é a fórmula para calcular a média aritmética entre dois números (ilustrada abaixo).

media = (a+b)/2

Os parênteses garantem que a soma a + b será executada antes da divisão por 2, e isso é necessário porque a divisão tem precedência sobre a soma.

Para explicar porque a variável exemplo2 ficou com o valor true quando recebeu o resultado da expressão (d or c) and not a no Exemplo de código 5, precisamos levar em consideração a precedência de operadores. Observe, no quadro de precedência de operadores, que o operador de atribuição (=) tem precedência sobre os operadores not, or e and, portanto a atribuição é executada primeiro.

Agora vamos repensar a execução de exemplo2 = (d or c) and not a levando em consideração a precedência de operadores. A primeira parte da expressão a ser executada é (d or c) porque ela está entre parênteses, e o resultado será true. Pela precedência de operadores, o próximo operador a ser executado é a atribuição (=), e é por isso que a variável exemplo2 recebe o valor true (resultado de d or c). O próximo operador a ser executado é o not, portanto not a resulta em false. Por fim, o que resta da expressão é true and false, que resulta em false.

Isso explica porque o resultado da expressão (d or c) and not a é false, mas quando se introduz o sinal de atribuição (exemplo2 = (d or c) and not a), a variável exemplo2 irá receber true, que é o resultado de apenas parte da expressão (d or c), e não dela toda.

Atividade 6.3

Crie três variáveis lógicas (com valores booleanos) e combine-as utilizando os operadores lógicos. Tente prever o resultado da expressão lógica antes de executá-la no IRB.

Referências

RUBY DOC. Precedence. Disponível em: https://ruby-doc.org/core-2.4.1/doc/syntax/precedence_rdoc.html. Acesso em: 05 jul. 2017.

POINT, T. Ruby Operators. Tutorials Point. 2015. Disponivel em: http://www.tutorialspoint.com/ruby/ruby_operators.htm. Acesso em: 03 nov. 2015.

SOUZA, L. Ruby: aprenda a programar na linguagem mais divertida. São Paulo: Casa do Código, 2012. v. 1.