JAVA: Boolean Logic



Relational Operators

Relational Operators are used to compare two primitive values to determine equality and/or order. Each of the following operators, when placed between two literals or variables, will produce a boolean value of TRUE or FALSE. The Java relational operators are: Notice that the comparison for equality is two equals signs (==). Be careful not to confuse this with assignment (=), as the results will be unreliable.
int n1=5;
int n2=7;
System.out.println(n1>n2);	//false;
System.out.println(n1<n2);	//true;
System.out.println(6>=8);	//false;
System.out.println(6<=8);	//true;
System.out.println(n1!=5);	//false;
System.out.println(n1==5);	//true;

These operators DO NOT work on the String type or any reference type variables. This is because of how primitives and reference types are stored.
The equality operator (==) checks and compares the value that is stored in the variable space. This works for primitives as they store their value, but reference variables store another location. So, even if the reference variables have the same information, they are pointing to different locations.


Instead, we have to use methods provided by the reference variable classes. Most programmers/designers will decide what it means for two of their objects to be equal (.equals) OR for one to be 'greater' or 'less' than another (.compareTo). Typically, both the equals and compareTo methods are provided in a class and take another variable of the same type as a parameter.

The .equals() Method
The .equals method is called from a reference type variable and takes another of the same type as a parameter. The result of the call is a boolean value of TRUE or FALSE.
String name="Sally";
System.out.println(name.equals("Earl"));		//false
System.out.println(name.equals("Sally"));	//true		


The .compareTo() Method
The .compareTo method is called from a reference type variable and takes a similar type as a parameter (r1.compareTo(r2)). The compareTo method returns an integer: We can then use the relational operators compared to 0 to determine a value of TRUE or FALSE.
In the String class, the compareTo method orders the Strings alphabetically.


String input="apple";
System.out.println(input.compareTo("apple"));	//0
System.out.println(input.compareTo("aaron"));	//15
System.out.println(input.compareTo("banana"));	//-1 
In the above examples, note that when compared to itself, the result is 0. When compared to something smaller( alphabetically before), the result is positive (because the calling object is bigger) and when compared to something larger (alphabetically after), the result is negative (because the calling object is smaller). The actual number is irrelevant - we only need to know if it is positive, negative or zero.

Logical Operators

The three logical operators we will focus on are AND(&&), OR(||), and NOT(!). These operators can be used to combine other boolean relations.

AND
Given two statements:
  1. the world is round
  2. grass is green
We qualify both of these statements with a boolean value of true. We can combine them with an AND to make this statement:

The world is round AND the grass is green.
Since both statements are true, the entire statement is TRUE.
Let's take a look at a few other examples of AND statements:
The world is round AND the grass is blue
The world is flat AND the grass is green.
The world is flat and the grass is red.
In line(1), the first part of the statement is true, but the second part is false. So, the whole statement evaluates to FALSE.

In line(2), the first part is false and the second part is true. The whole statement evaluates to FALSE.

In line(3), both parts are false so the statement is FALSE.

An AND statement evaluates to true if and only if BOTH parts of the statement is true.
Now, we will take a look at this in code. One of the most important things about writing an AND statement, also known as a conjunction, is that both sides of the statement MUST be able to stand alone as boolean statements. In other words, each side of the conjunction must, on its own, evaluate to true or false. So, if we want to check if a user age is equal to 18 and that their zip code is equal to 19446, we would write the following:

age==18 && zip==19446
Notice how age==18 will evaluate to true or false based on the value of age, as will zip==19446.

OR
We will keep the same two statements from our previous example and this time, we will combine them with an OR statement, also known as a disjunction. Here are several statements combined using OR:
The world is round OR the grass is green.
The world is round OR the grass is purple.
The world is a cube OR the grass is green.
The world is a pyramid OR the grass is orange.
In line(1), both parts are true so the statement evaluates to TRUE.

In line(2), the first part is true and the second part is false. But, because it is an or, the entire statements is TRUE.

In line(3), the first part is false and the second part is true. Again, the statement evaluates to TRUE.

It is only in the fourth example, line(4), that we find the statement to be false, because both parts are false.

An OR statement evaluates to true if either part is true.


When writing code, the same is true in a disjunction as in a conjunction, both parts MUST be able to evaluate to true or false on their own. Let's look at an example where we want to know if a user's input is a 4 or a 5:
input==4 || input==5
A common mistake is that students write input==4 || 5. This is NOT correct as the right side (5) DOES NOT evaluate to true or false as it is not a standalone condition.


NOT
The NOT operator (!), also known as negation, changes the boolean value of the expression. If the statement was true, it is now false, and vice-versa.
!(5==5)
In the above example, the value of the statement is false because 5==5 is true and the negation of that is false.
The negation operator can have a more interesting effect on compound conditions. De Morgan's Law, a rule of logic, states the following (assuming a and b are boolean relations):
  1. !(a&&b) => !a || !b
  2. !(a||b) => !a && !b
Notice that the negation outside the parenthesis not only negates the boolean relations, but also changes a conjunction to a disjunction and a disjunction to a conjunction.

Using DeMorgan's Law, the following two statements are equivalent:
!(input==4 || input==5)
input!=4 && input!=5


Truth Tables

Truth tables are used to evaluate the circumstances under which a boolean/logical relation is true or false. After using a truth table to assess a statement, we can use that table to generate test data to make sure that our program works the way it is intended.

To begin, truth tables are a key element of a branch of mathematics called logic. Often, we use symbolic logic to shorten the amount of writing that we need to do. Symbolic logic works by substituting a variable for a boolean expression. For example, if we are given the following condition:
age>=16 && testScore>=80


Using symbolic logic, we can declare the following variables:
The variables p and q are chosen randomly. You can use any letters that you choose.


Using symbolic logic we can now represent the same statement using
p && q
Each of these statements can have a value of true or false. So, together, we have four different possibilities:
pq
TT
TF
FT
FF
Building a Truth Table
The number of rows in a truth table is determined by 2#variables. In this case, there were two variables, so we get 22=4 rows.

To avoid missing any combinations, we can set up our T/F values in each column using the following process:
  1. In the first column, take the number of rows/2. Write that many T rows followed by the same number of F rows.
  2. In the next column, take the number of T from the first column and cut it in half, alternating sets of that number of T followed by that number of F, until all the rows are filled.
  3. This process of halving repeats until the final column is reached, at which point the values alternate T and F in each row.
Here is an example using the following statement with three variables (8 rows):
grade==12 || (prereq>=90 && grade>=10)


abc
TTT
TTF
TFT
TFF
FTT
FTF
FFT
FFF
Explanation:
We begin by taking each of the individual boolean relations and assigning them a variable:
a = grade==12
b = prereq>=90
c = grade>=10

Then, with three variables, we know that there is 23=8 rows.

The first column will have 8/2=4 consecutive rows of T followed by 4 consecutive rows of F.

The second columns will have 4/2=2 rows of T follwed by 2 rows of F. That pattern will repeat until out of rows.

The final column will alternate T/F on each row.

Evaluating a Truth Tables
Once we have built the rows of a truth table, we can begin evaluating the truth value of each row. This happens by applying the truth value in each row to the variables in the expression and determining the boolean value of the statement for that row. In this example, that is easy, as the parenthesis give us a clear order of operations.

Often, in more complicated truth tables, we can build the process in steps.
abcb&&ca||(b&&c)
TTTTT
TTFFT
TFTFT
TFFFT
FTTTT
FTFFF
FFTFF
FFFFF
Explanation:
Step 1: Using only the values from the b and c columns, we generate the value of (b&&c) for each row. In the blue highlighted example, you will see that T && T => T.

Step 2: Once the fourth column is complete, we use the a column and the (b&&c) column to generate the last column, which is the value of the truth table. In the red highlighted example, a=F and (b&&c)=T. When combined with an OR statement, the final result is T.

What we now know is the truth value of the statement for each possible set of boolean relational values. For example, if all of the original conditions are false, the statement will be false. However, if the first value is true (grade==12), it doesn't matter what the other values are, the statement is true.


Using a Truth Table to Generate Test Data
Given the result of the truth table above, we can use this to generate data to test our program. As an example, let's take a look at the 5th row.

abca||(b&&c)
FTTT
In this example, a is false, b is true and c is true. This means that when we run the program, we need data that meets the following conditions:
  • grade!=12 (a is false)
  • prereq>=90 (b is true)
  • grade>=10 (c is true)
So, we can set grade=11 and prereq=95. These pieces of data should produce a result of True when the program runs.

© M. Engel, 2024