Aula 8: Selecção/Ramificação II / Álgebra Booleana

Traduzido por Ana Paula Costa 

Álgebra Booleana

George Boole (1815-1864)

English mathematician. His work "The mathematical analysis of logic"  (1847) established the basis of modern mathematical logic, and his boolean algebra can be used in designing computers.
Boole's system is essentially two-valued. This can be symbolized by
0     or   1   "binary representation"
TRUE  or FALSE  "truth representation"
0 V   or  5 V   "TTL electronics (transistor-transistor logic)"
0 pC  or  1 pC  (pC = pico-Coulomb)  "the charge in a condensator, the elementary memory unit in (dynamic) RAM"
 

Na aula anterior (aula 7) vimos como se pode controlar o fluxo de um programa utilizando as instruções de selecção if ... e if ... else .... Usamos condições como (x<1.0). Imaginemos agora que queremos calcular a raíz quadrada de (x2-4). Claramente, não existe resposta para x entre -2 e +2. É importante verificar se x se encontra neste intervalo ou não. Podemos resolver a situação assim:
  if (x<2)
    if (x>-2)
      printf("Error");
Mas seria muito melhor se pudéssemos fazê-lo com uma única condição. E tal é possível, vamos ver como:
  if ((x<2) && (x>-2))
    printf("Error");
o que significa que ambas as condições (x<2 e x>-2), devem ser verdadeiras para que a condição completa seja verdadeira.
Este é um exemplo de um cálculo Booleano
  condição3 = condição1 && condição2;
  if condição3
    instrução;

operadores lógicos em C

&& AND (E)
|| OR (Ou)

Um outro operador lógico importante é o XOR, embora não seja implementado nos cálculos Booleanos em C.  a XOR b significa "a ou b verdadeiros, mas não ambos!". Iremos utilizar o XOR mais tarde nas operações lógicas com inteiros. Outros operadores não implementados incluem o NAND e o NOR que são importantes apenas ao nível electrónico, não sendo muito úteis ao nível da programação.
Finalmente, existe um negador booleano !. Significa obter o oposto; Se a é falso (0), !a é verdeiro (1), e vice versa. Enquanto && e || necessitam de dois operandos (por exemplo a || b), ! necessita apenas de um (!a).
Com estes operadores podemos calcular todas as condições possíveis que precisemos.

Aqui estão as tabelas completas ("tabelas de verdade") dos cálculos para os quatro operadores utilizados nas linguagens de programação modernas.
 
 Operador Booleano 
C
AND (E)
&&
OR (Ou)
||
XOR (Ou exclusivo)
 
NOT (Negação)
!
AND (&&)
a
b
 a AND b 
true
true
true
true
 false 
false
 false 
true
false
 false 
false
false
OR (||)
a
b
a OR b
true
true
true
true
false
true
false
true
true
 false 
 false 
 false 
XOR
a
b
 a XOR b 
true
true
false
true
false
true
false
true
true
 false
 false 
 false 
NOT (!)
a
NOT a
true
 false 
 false 
true
Exemplos:

  a = 1;
  b = -2;
  c = 3;
  d = 3;

  if (a>0)
    printf("TRUE");
  else
    printf("FALSE");

(a>0)                  TRUE
(b>0)                  FALSE
(a>0) && (b>0)         FALSE
(a>0) || (b>0)         TRUE
!(a>0)                 FALSE
(! (a>0)) || (b>0)     FALSE
(2==b) || (!(2==b))    TRUE


Álgebra Booleana para inteiros

Também se pode aplicar a álgebra booleana a números completos (char, int, long int, ver aula 5). Embora não fizesse parte da ideia original de Boole, podemos facilmente realizar o mesmo tipo de cálculos com números, desde que seja feito "um bit de cada vez" e se utilize a representação "1 = verdeiro" e "0 = falso". Para evitar confusões, os símbolos para operações com números são diferentes dos que foram dados anteriormente, nomeadamente "&" para "AND", "|" para "OR", "^" para "XOR" e "~" para "NOT".
 
 operation  C 
 AND
&
 OR
|
 XOR
^
 NOT
~

Com esta convenção, as tabelas de verdade para a álgebra booleana bit-a-bit (bit-wise) ficam assim:
AND (&)
 a 
 b 
 a AND b 
1
1
1
1
 0
0
 0
1
0
 0
0
0
OR (|)
 a 
 b 
 a OR b 
1
1
1
1
0
1
0
1
1
 0
 0
 0
XOR (^)
   a 
   b 
 a XOR b 
1
1
0
1
0
1
0
1
1
 0
 0
 0
NOT (~)
 a 
 NOT a 
1
0
0
1
49 = 
0 0 1 1 0 0 0 1  
24 = 
0 0 0 1 1 0 0 0  
  49 | 24 = 
0 0 1 1 1 0 0 1  = 57 
49 & 24 =
0 0 0 1 0 0 0 0  = 16
49 ^ 24 =
0 0 1 0 1 0 0 1  = 41
~49 =
1 1 0 0 1 1 1 0  = 206
~24 =
1 1 1 0 0 1 1 1  = 231
Como exemplo, vamos imaginar que queremos calcular 49 OR 24. 
Primeiro, temos que converter estes números para o sistema binário (ver aula 3): 
  49 = 1*32 + 1*16 + 0*8 + 0*4 + 0*2 + 1*1 = 110001 
  24 = 0*32 + 1*16 + 1*8 + 0*4 + 0*2 + 0*1 = 011000 
Depois, fazemos os cálculos bit-a-bit com as convenções da tabela de verdade de cima (1 OR 1 = 1, 1 OR 0 = 1, 0 OR 1 = 1, 0 OR 0 = 0), o que dará  111001 
Finalmente, convertemos o resultado para o sistema decimal 
  111001 = 1*32 + 1*16 + 1*8 + 0*4 + 0*2 + 1*1 = 57 

Na tabela ao lado, são exemplicadas também as outras operações com os números 49 e 24.

De notar que o operador ~ (bit-wise inverter) depende do tamanho da variável:
Para um byte (unsigned char) ~49 = ~(00110001) = (11001110) = 128 + 64 + 8 + 4 + 2 = 206, enquanto para um unsigned int ~49 = ~(0000000000110001) = (1111111111001110) = 65486.


Selecção/Ramificação Múltipla: switch

Na aula anterior (ver aula 7) vimos como utilizar a instrução if ... para seleccionar entre duas possíveis partes de um programa. Em alguns casos, podemos ter mais do que dois caminhos possíveis para o programa continuar. Para estas situações existe a instrução switch .
Vamos imaginar o programa que se segue que pergunta que ano o aluno frequenta:

  main()
   // outputs the lectures to follow on basis of year
  {

    int ano;

    printf("From what year are you?\n");
    scanf("%d", &ano);
    if (ano==1)
      printf("primeiro ano: MAT-1, CALC-1\n");
    else
      if (ano==2)
        printf("segundo ano: INF, LIN-ALG\n");
      else
        if (ano==3)
          printf("terceiro ano: ELEC, FYS\n");
        else
          if (ano==4)
            printf("quarto ano: QUI, MAT-2\n");
          else
            if (ano==5)
              printf("quinto ano: PROJECTO\n");
            else
              printf(">5: AINDA NAO ACABOU?\n");
  }

(De notar a estrutura do programa, com indentações. Reparar também na ausência de ; antes de todos os else).
Este programa irá correr sem qualquer problema
From what year are you?
 1
primeiro ano: MAT-1, CALC-1

From what year are you?
 4
quarto ano: QUI, MAT-2

Apesar disso, a sua estrutura não é muito legível. Para tornar o programa melhor (mais legível) podemos utilizar a instrução switch. Enquanto em "if (condição) instrução1;" a condição é forçosamente do tipo boolena (verdeiro ou falso), com o switch podemos utilizar qualquer um dos tipos de variáveis que têm valores discretos (em contraste com as variáveis floating point que não têm valores discretos).
 

 switch (expressão)
 {
   case valor1: instrução1; 
        break;
   case valor2: instrução2;
        break;
          |
   case valorN: instruçãoN;
        break;
   default: instruçãoE;
 }

A expressão tem que resultar num valor de um tipo contável (por exemplo: int, long int, mas também char. Não pode ser, por exemplo, float). Tal pode ser uma simples variável ou um cálculo que resulte num valor. Os valores valor1 a valorN têm que ser do mesmo tipo, mas não podem conter expressões ou variáveis; têm que ser do tipo constant (constante).
A palavra reservada "default" significa que será executada esta instrução se a expressão não resultar em nenhum dos valores valor1.. valorN.
A instrução break força o programa a saltar para o fim do ciclo (loop) (um ciclo de switch neste caso).
Como exemplo, segue-se o programa anterior reescristo para utilizar a instrução switch:

  main()
   // same program as above, but with switch statement
  {
    int ano;

    printf("From what year are you?\n");
    scanf("%d", &ano);
    swicth (ano)
     {
      case 1: printf("primeiro ano: MAT-1, CALC-1\n");
              break;
      case 2: printf("segundo ano: INF, LIN-ALG\n");
              break;
      case 3: printf("terceiro ano: ELEC, FYS\n");
              break;
      case 4: printf("quarto ano: QUI, MAT-2\n");
              break;
      case 5: printf("quinto ano: PROJECTO\n")
              break;
      default: printf(">5: AINDA NAO ACABOU?\n");
     }
  }

Este programa terá o mesmo output que o programa anterior, mas é muito mais legível.

Aula 8:    (2*B) || (! (2*B)) 


Teste Rápido:
Para testar os conhecimentos sobre o que aprendeu nesta aula, prima aqui para fazer um teste on-line. De notar que este Não é o formato que será utilizado no teste final!

Peter Stallinga. Universidade do Algarve, 28 October 2002