Logic

The possible Boolean values are True and False. Python has three logical operators, which manipulate Boolean values: not, and and or. The formal logical meaning of these was introduced in class and in our mathematical text.

It is important to note that in programming that the in a two part expression such as P and Q, the second part Q is only evaluated if necessary to determine the truth of the whole expression.

The following is a listing these logical operators together with example expressions. Here P and Q are values which are either True or False and n is an integer.
Operator Syntax Example expression
not not P not n==0
(same as n!=0)
and P and Q n!=0 and abs(2/n)>1
(This will run fine, the second expression is only evaluated if n!=0 is true.)
or P or Q n==0 or abs(5/n)<2
(This will run fine, the second expression is only evaluated if n=0 is false.)

Here are some examples of running the example expressions:

In [1]:
n=5
print("When n is", n, "the value of not n==0 is", not n==0)
n=0
print("When n is", n, "the value of not n==0 is", not n==0)
When n is 5 the value of not n==0 is True
When n is 0 the value of not n==0 is False

In [2]:
n=3
print("When n is", n, "the value of n!=0 and abs(2/n)>1 is", n!=0 and abs(2/n)>1)
n=-1
print("When n is", n, "the value of n!=0 and abs(2/n)>1 is", n!=0 and abs(2/n)>1)
n=0
print("When n is", n, "the value of n!=0 and abs(2/n)>1 is", n!=0 and abs(2/n)>1)
When n is 3 the value of n!=0 and abs(2/n)>1 is False
When n is -1 the value of n!=0 and abs(2/n)>1 is True
When n is 0 the value of n!=0 and abs(2/n)>1 is False

In [3]:
n=2
print("When n is", n, "the value of n==0 or abs(5/n)<2 is", n==0 or abs(5/n)<2)
n=-12
print("When n is", n, "the value of n==0 or abs(5/n)<2 is", n==0 or abs(5/n)<2)
n=0
print("When n is", n, "the value of n==0 or abs(5/n)<2 is", n==0 or abs(5/n)<2)
When n is 2 the value of n==0 or abs(5/n)<2 is False
When n is -12 the value of n==0 or abs(5/n)<2 is True
When n is 0 the value of n==0 or abs(5/n)<2 is True

Note that in the expressions for and and or, in the case when \(n\) is zero, the second expressions are not evaluated. You can tell they are not evaluated because evaluating these expressions results in an error such as:

In [4]:
n=0
abs(5/n)
---------------------------------------------------------------------------
ZeroDivisionError                         Traceback (most recent call last)
<ipython-input-4-56a9d3c983ed> in <module>()
      1 n=0
----> 2 abs(5/n)

ZeroDivisionError: division by zero

Example.

The following example decides if a number is both even and positive. It either prints out that the number is both even and odd, or explains why this statement is not true.

In [5]:
def even_and_positive(n):
    if n%2==0 and n>0:
        print(n, "is both even and positive.")
        return
    if not n%2==0:
        print(n, "is not both even and positive because it is odd.")
        return
    print(n, "is not both even and positive because it is not positive.")
In [6]:
even_and_positive(10)
even_and_positive(5)
even_and_positive(-12)
even_and_positive(-7)
10 is both even and positive.
5 is not both even and positive because it is odd.
-12 is not both even and positive because it is not positive.
-7 is not both even and positive because it is odd.

Remark (Empty return statement and None): In the definition of the function even_and_positive(n) you see two empty return statements. These just stop evaluation of the function. They actually return the None object which just indicates that nothing was returned. So the line "return" is the same as the line "return None".

Remark (Operator Precedence): You'll notice a general lack of parentheses in the expressions above. Python evaluates numerical operations and comparisons before evaluating logical operators. This is explained in the section on Operator Precedence in the Python Reference. If you have concerns about the order in which an expression is evaluated, include parenthesis to ensure things are evaluated in the correct order.

Example (Exclusive Or)

Here is a problem similar to your homework:

The exclusive or logical operation takes as input Boolean values and returns True or False. It returns True if one of the two expressions is true and the other is false (in any order). It returns False if the two truth values match. Write a function named xor which takes as input two boolean values P and Q and when evaluated as xor(P,Q) returns the value of P xor Q.

The following would be a correct answer to the problem:

In [7]:
def xor(P,Q):
    return (P or Q) and not (P and Q)

Truth Tables

Suppose truth_function is a function which takes as input two boolean values P and Q and returns a new Boolean value. Then we can print out a truth table for truth_function. Here is a function which does this:

In [8]:
def print_truth_table(truth_function):
    print(" P    |  Q    |  ", truth_function.__name__,"(P,Q)", sep="")
    for P in (True, False):
        for Q in (True, False):
            print(str(P).ljust(5), "|", str(Q).ljust(5), "|",str(truth_function(P,Q)).ljust(5))

Remarks: This function is somewhat more advanced than what we have done yet, and I would not expect you to write this on your own yet. Let me explain some things you have not seen.

In the first print expression, you see truth_function.__name__. This returns the name of the function truth_function. For example, if we pass the xor function defined above, then truth_function.__name__ is the string "xor". Typically the print function separates adjacent arguments (e.g., "x=" and x above) by a space. This can be changed with the special sep argument. Above the separator was changed to an empty string (which means no separator). You can learn more about the print() function in the Python library documentation and the Python tutorial.

The second and third lines of the print_truth_table function, you see for loops. For example, the line "for P in (True, False):" repeatedly runs the indented block below twice, first with P=True and second with P=False. This idea will be introduced when we discuss lists in Python.

The last print expression is inside two loops, and so will be evaluated four times with all possible values of P and Q. The line is essentially the same as print(P,"|","Q","|",statement(P,Q)) except for some details about spacing. The table printed in this way looks ugly because "True" has four letters while "False" has five. If x is any python object, str(x) returns an expression for x as a string of text. Then, str(x).ljust(5) pads the string with spaces on the right if necessary for the string to have length at least \(5\). So str(True).ljust(5) produces "True " (with one extra space) while str(False).ljust(5) produces "False". You can learn more about str() and ljust().

In [9]:
print_truth_table(xor)
 P    |  Q    |  xor(P,Q)
True  | True  | False
True  | False | True 
False | True  | True 
False | False | False

Here are some truth tables for and and or.

In [10]:
def and_function(P,Q):
    return P and Q
print_truth_table(and_function)
 P    |  Q    |  and_function(P,Q)
True  | True  | True 
True  | False | False
False | True  | False
False | False | False

In [11]:
def or_function(P,Q):
    return P or Q
print_truth_table(or_function)
 P    |  Q    |  or_function(P,Q)
True  | True  | True 
True  | False | True 
False | True  | True 
False | False | False

Exercises:

  1. Write a function named implies(P,Q) that takes as input two boolean values P and Q and returns the truth value of the statement "P implies Q".
  2. Write a function named iff(P,Q) that takes as input two boolean values P and Q and returns the truth value of the statement "P iff Q".
  3. Say that a \(2\)-input truth function is a function which takes as input two boolean values P and Q and returns a boolean value. (Examples include xor above, as well as implies and iff from the problems above.)
    Write a function named tautology(tf) that takes as input a \(2\)-input truth function tf and returns True if tf is a tautology and returns False otherwise. (That is, the function should return True if tf returns True for all boolean inputs).
  4. Write a function named logically_equivalent(tf1, tf2) that takes as input two \(2\)-input truth functions tf1 and tf2 and returns the boolean value of the statement "The two truth functions are logically equivalent."

References:

  • How to Think Like a Computer Scientist: Learning with Python 3 has a section on logic.