Logical Tests and the Nature of Truth


Topics:

  • if tests
  • and, or, and not logic

Introduction


So, we've now spent a few action-packed sessions learning about python, and we've learned quite a bit. We know what variables are, how to print them, and how to store and manipulate more complex data in more complex data structures. However, we're still missing one of the most essential components of programming: logic.

The objective, when you begin programming, is to make the computer do your work for you. Instead of normalizing that microarray data or computing that crystal structure fit with a pocket calculator and a bucket of coffee, you want to be able to say, 'Computer, do my work,' and then have it done. Of course, you'd have to say it a little more clearly than that -- for instance, to pick out over-expressed membrane proteins from microarray data, you could tell the computer that for each gene in the genome:

"If the protein is located in the membrane, tell me if that protein's expression level under a given set of experimental conditions [stored in column 2, for example] is 5-fold or more greater than the expression level under control conditions [stored in column 4, for example]."

Q: What's the magic word there?
A: if

Indeed, the if statement is one of the most fundamental components of programming, and it is found in some form or another in every general-purpose programming language (and nearly all specialized ones, too). It simply tells the computer to execute a block of code if a given statement is true. If the statement is false, the code is skipped. Although the concept is simple, conditional statements like if allow your programs to make decisions on their own.

The Syntax of IF


In Python, we construct our if statements with this general form:


if <insert something that may or may not be true here>:
execute this line of code
execute this line of code too

The key pieces of syntax here are the if, the condition that we would like to evaluate (note: the condition is expressed within brackets <> for emphasis in this example, but brackets are not used in real code), the colon : that signifies the end of the logical test, and the tab-indentation for the things to do below the first line of the statement.

We will discuss what makes things True in a minute, but for now we should focus on the syntax. Beyond the if and the colon, python will execute all of the following indented code. Indentation is the signal to the interpreter that this block of code is under the control of the if statement.

Speaking of decision making, we could use this syntax to write a program that spares us the trouble of deciding what to eat each week:

#!/usr/bin/env python
 
checkingAccountBalance = 4
 
# Let's imagine that I have to eat crappy ramen if I
# have less than $10 at the start of the week, but I
# can eat the fancier kind if I have more...
 
if checkingAccountBalance < 10:
    thisWeeksFood = 'ramen'
if checkingAccountBalance >= 10:
    thisWeeksFood = 'goodRamen'
 
print "We are eating %s this week" % (thisWeeksFood)
 
# But maybe if I have exactly $15, I can get the
# very best ramen.
 
if checkingAccountBalance == 15:
    thisWeeksFood = 'that particular brand of good ramen'
 
if checkingAccountBalance != 15:
    thisWeeksFood = 'certainly _not_ that brand!'
 # if statements can contain more than one command
    print 'absolutely not!'
 

Not bad for a start, but we'd like to clean that up a bit. Not only is "checkingAccountBalance <= 10" a pile of unnecessary keystrokes, it could also hurt us down the road. For instance, what if inflation pushed up the price of good ramen? We'd want to change out cutoff point s-- to twelve bucks, for instance. However, if we changed the numbers in just the first if statement, but forgot to change the second, we'd have a bug in our code. By making our program simpler we can avoid such mistakes, and we can make things simpler by using python's else statement.

if checkingAccountBalance < 10:
    thisWeeksFood = 'ramen'
else:
    thisWeeksFood = 'goodRamen'

But what if there's a third option? Or a fourth? This is taken care of by python's elif statement (short for "else if"). elif tells python that the indented code immediately following the elif statement should only be executed if the preceding if or elif statement(s) evaluated to false.

if checkingAccountBalance < 10:
    thisWeeksFood = 'ramen'
elif checkingAccountBalance < 100:
    thisWeeksFood = 'good ramen'
elif checkingAccountBalance < 200:
    thisWeeksFood = 'better ramen'
else:
    thisWeeksFood = 'ramen that is truly profound in its goodness'

As a rule, it's good practice to account for all possibilities in your if, elif, else statements. In the case of our $10 budget, we initially cared only if we had $10 or more, and so this might be a safe place to use the if, else construct to account for all possibilities. But what if we actually had a zero balance in our checking accounts, and thus had no money to exchange for noodles? (Don't worry, you can always steal cookies at the departmental seminar.) We've accounted for all positive values with our above logic, but we've left an implicit assumption that having $0 or less on our checking account balance still results in our eating ramen this week. The point: we often make oversights that python will not overlook. These can result in interesting, though nonsensical outcomes -- like getting ramen for nothing. The lesson is to be careful and thorough as you compose your logical statements.


external image bug.png



At least we know what's for dinner.

The Nature of Truth: Sneaking philosophy into computer science


But what is truth, anyway? Here we have a simple statement, "less than," and it's meaning is intuitive. But truth has a formal meaning in python, as it does in all programming languages. In short:

Any nonzero number, nonempty object, and the special object True are things Python believes to be True.
Zero numbers, empty objects, and the special objects None and False are things Python believes to be False.

Observe:
if 1:
    print "True!"
 
if 0:
    print "True!"
else:
    print "False!"

Of course, these operations will be even more useful if we can use variables instead of straight numbers.

s = 0
if s:
    print "s is True!"
else:
    print "s is False!"

What if we want the variable s to evaluate to True if s is zero? Employ the == logical operator. == evaluates whether the value of the two operands on either side are equal or not, and if they are, the statement evaluates to True.

You can find more information about operators, like ==, at this site (which I found just by Googling "python logical operators").

s = 0
if s == 0:
    print "s is True!"
else:
    print "s is False!"

Non-empty strings are always true, no matter their content.

s = ""
if s:
    print "Your instructor has done something wrong"
else:
    print "'s' is surely not True, because 's' is an empty string."
 
 
s = "Programming in Python is *waaay* better than pipetting!"
if s:
    print "'%s' is True!" % s
else:
    print "'%s' is False!" % s

Python has abstract versions of truth and falsehood as well.

# We can store the result of a logical test in a variable.
 
s = (2 + 2) == 4
print s
 
if s:
    print "True!"
else:
    print "False!"
 
 
s = (2 + 2) == 5
print s
 
if s:
    print "True!"
else:
    print "False!"

In Python, we also have a third notion, None. This is less common, and it evaluates to False when used in an if statement. We'll see more of it later when we talk about writing our own functions.

s = None
if s:
    print "Nothing is true!"
else:
    print "Nothing is false!"

and, or, (and) not


Finally, what if we want to evaluate more than one if statement at once? The Boolean operators and, or, and not can help us out. The operator and will only return True if both accompanying statements are True, while or will return True if either accompanying statement is True. not returns the inverse of the logical value of the statement given.

true_statement = 'truth itself' # non-empty strings are true
false_statement = '' # empty strings are false
 
if true_statement and false_statement:
    print "This should not print."
else:
    print "This should print."
 
if true_statement or false_statement:
    print "This should print."
 
if not false_statement:
    print "This should print."
 
if not (true_statement and false_statement):
    print "This should print."
 

However, and and or are a little more complicated than, for instance, the > and == operators that we're used to seeing in if statements. Let's look at and first:

a = 5 < 3
print a
 
b = 1 and 1
print b
 
c = 1 and 0
print c
 
d = 1 and 2
print d
 
e = 2 and 1
print e
 
f = 0 and 2
print f
 

The and operator requires that both values in the expression evaluate to True values. But instead of returning True or False, the and operator continues evaluating the logical expression until it reaches either the end of the expression or a False value, at which point it returns the last value it evaluated. In other words, and returns the first False value it sees, or else it returns the last thing it sees, which must be True if nothing was False.

Think about this by imagining the computer looking at each statement in turn, from left to right, being maximally lazy about the number of statements it wants to look at and maximally forgetful about the number of statements it has to remember. For instance, when it's given the 'd = 1 and 2' statement, it first looks at 1, figures out that it's True, and then it checks the 2 -- only remembering the 2, it returns that. It's the reverse case with the 'e = 2 and 1' statement: as it looks at the 1 last, and so returns 1. And finally, with the 'f = 0 and 2' statement, it knows that the 'and' can't possibly be true if the first part is false, so it just returns the only thing that it has seen -- the 0.

The or works in a similar manner, evaluating the values in the expression until it either reaches the end of the expression or reaches a True value, at which point it returns the last value it evaluated.

g = 0 or 1
print g
 
h = 1 or 0
print h
 
i = 1 or 2
print i
 
j = 2 or 1
print j
 

In the first case, the computer needs to see the second variable to evaluate the or, so it returns the second variable. In the second through fourth cases, it knows that the or should be true after only seeing the first variable, and so it returns that.

Order of Operations with Logical Tests


We can make expressions of logic arbitrarily complicated, and so it's good to know where on the Great Chain of Operations these logical operators fall.

a = 1 and 2 + 5
print a
 
a = (0 and 2) + 5
print a
 
a = not (0 and 2) + 5
print a
 
a = 5 and not (0 and 2)
print a

You can find more information about the order of operations in python at this site, for example (which I found just by Googling "python order of operations").


Exercises:


1: Truth or Consequences


Use the following format to evaluate the truth of some expressions in python:

query = <fill in blank>
 
if query:
    print 'Query is true'
else:
    print 'Query is false'

Try the following things in the blank. Are they True? If you don't see exactly why they are True or False, please ask a TA.

A) "" (the empty string)

B) []

C) {}

D) (a list containing an empty list)

E) 0

F) [0]

G) [0][0] (also-- what is this? Print out the value of [0][0] and [1][0] before doing the truth test)

H) [0]


2: Discourse on the Origins and Foundations of Inequality Among Numbers


A) Have the user input two numbers. If the numbers are equal, print the number back.

B) Modify your solution of Exercise #2 such that if the numbers are not equal, print the first number that the user entered back. Use the else statement.

C) Modify your solution of Exercise #3 such that if the numbers are not equal, print the smaller number. Use the elif statement.


3: My Pet Gene Finder


Write your first gene-finding program: have the user enter a string of six nucleotides. If the length of the string is not six, tell the user to input another string. If it is, then create the four three-base substrings of this string. If any of these substrings is the sequence 'ATG', print the position of that substring (If 'ATG' appears multiple times, print only the location of the first instance of 'ATG'). If none of the substrings match 'ATG', print -1.

Your gene finder isn't very good at finding genes yet, but it is good at motivating some of the things that you'll be learning about in the next two lectures: loops, which perform a single task over and over and over again, and python's text processing capabilities, which will reduce the bulk of this program to a single line.


4: Baby's First FASTA Parser


Write your first FASTA parser: from a series of user inputs, you wish to create a dictionary keyed by gene IDs with values corresponding to their sequence. A FASTA file has the following format:
>gene1
ATAGCAGTTAGC
TTAGCAGCAGTT
ATAGCGCA
>gene2
ATGACGACGATT
TTGACGACTAGG
ACAGCC
>gene3
AGATGCCCCCTT
...
While you will learn about handling files soon, for now, assume that this file is being typed in by the user -- their first input will be '>gene1', their second will be 'ATAGCAGTTAGC', and so on. Ask the user for input 6 times (3 genes and 3 corresponding sequences). For each line input, you will have to determine whether it is a gene name or a sequence and act accordingly. Assume that the gene names and sequences will be entered in pairs (i.e. if the first input was a sequence, then the next will be the corresponding gene name, etc).

Your dictionary should have keys 'gene1', 'gene2', and 'gene3' (note the absence of the '>' symbol) and the sequences of those genes as values.


5: Operator, Give Me the Number for 911


Replace "X" in this code with a statement that ensures that if the user entered "1", a "0" is printed, and if they entered a "0", a "1" is printed. Don't worry about other numbers.
s = raw_input("Enter a number: ")
s = int(s)
t = X
print t
 

Solutions


1: Truth or Consequences

# "" is False
 
# [] is False
 
# {} is False
 
# [ [] ] is True, because the list isn't empty, it has a list in it!
 
# 0 is False
 
# [0]
 
# [0][0] is False, because [0] is a list, and [0][0] is the first position of the list, a.k.a., 0
 
# [ [] ][0] is False, because the 0th element of the list is an empty list.
 

2: Discourse on the Origins and Foundations of Inequality Among Numbers


A)
a = raw_input("Enter a number:  ")
 
b = raw_input("Enter another number:  ")
 
if a == b:
    print "These numbers are the same!"
 

B)
a = raw_input("Enter a number:  ")
 
b = raw_input("Enter another number:  ")
 
if a == b:
    print "These numbers are the same!"
else:
    print a, "is the first number you entered,  but these numbers are not equal!"
 

C)
a = raw_input("Enter a number:  ")
 
b = raw_input("Enter another number:  ")
 
if a == b:
    print "These numbers are the same!"
elif a < b:
    print a, "is the smaller of these numbers."
else:
    print b, "is the smaller of these numbers."
 
 

3: My Pet Gene Finder


hexamer = raw_input("Please enter a six-nucleotide sequence in all capital letters:  ")
if len(hexamer) != 6:
    print "Oh, hrmmmm... I'm fairly certain that sequence isn't of length 6."
    hexamer = raw_input("Please enter a six-nucleotide sequence in all capital letters:  ")
 
start = 0
stop = 3
 
if hexamer[start:stop] == 'ATG':
    print start, "is the position of the first Met codon."
else:
    start += 1
    stop += 1
 
if hexamer[start:stop] == 'ATG':
    print start, "is the position of the first Met codon."
else:
    start += 1
    stop += 1
 
if hexamer[start:stop] == 'ATG':
    print start, "is the position of the first Met codon."
else:
    start += 1
    stop += 1
if hexamer[start:stop] == 'ATG':
    print start, "is the position of the first Met codon."
else:
    print "-1"
 

4: Baby's First FASTA Parser

# a dict for genes
genes = {}
 
#1
line = raw_input("Enter a line:  ")
 
if line[0] == ">":
    gene = line[1:]
    genes[gene] = ''
else:
    genes[gene] += line
 
#2
 
line = raw_input("Enter a line:  ")
 
if line[0] == ">":
    gene = line[1:]
    genes[gene] = ''
else:
    genes[gene] += line
 
#3
 
line = raw_input("Enter a line:  ")
 
if line[0] == ">":
    gene = line[1:]
    genes[gene] = ''
else:
    genes[gene] += line
 
#4
 
line = raw_input("Enter a line:  ")
 
if line[0] == ">":
    gene = line[1:]
    genes[gene] = ''
else:
    genes[gene] += line
 
#5
 
line = raw_input("Enter a line:  ")
 
if line[0] == ">":
    gene = line[1:]
    genes[gene] = ''
else:
    genes[gene] += line
 
#6
 
line = raw_input("Enter a line:  ")
 
if line[0] == ">":
    gene = line[1:]
    genes[gene] = ''
else:
    genes[gene] += line
 
print genes
 
###ALTERNATE SOLUTION###
 
# a dict for genes
genes = {}
 
input1 = raw_input("Enter the gene name (format: '>genename') or sequence (all caps!):  ")
 
input2 = raw_input("If you entered the gene name above, enter the sequence here (and vice versa):  ")
 
if input1[0] == ">":
    gene = input1[1:]
    genes[gene] = ''
    genes[gene] += input2
elif input2[0] == ">":
    gene = input2[1:]
    genes[gene] = ''
    genes[gene] += input1
else:
    print 'You did not enter a gene name.'
 
input1 = raw_input("Enter the gene name (format: '>genename') or sequence (all caps!):  ")
 
input2 = raw_input("If you entered the gene name above, enter the sequence here (and vice versa):  ")
 
if input1[0] == ">":
    gene = input1[1:]
    genes[gene] = ''
    genes[gene] += input2
elif input2[0] == ">":
    gene = input2[1:]
    genes[gene] = ''
    genes[gene] += input1
else:
    print 'You did not enter a gene name.'
 
input1 = raw_input("Enter the gene name (format: '>genename') or sequence (all caps!):  ")
 
input2 = raw_input("If you entered the gene name above, enter the sequence here (and vice versa):  ")
 
if input1[0] == ">":
    gene = input1[1:]
    genes[gene] = ''
    genes[gene] += input2
elif input2[0] == ">":
    gene = input2[1:]
    genes[gene] = ''
    genes[gene] += input1
else:
    print 'You did not enter a gene name.'
 
print genes
 

5: Operator, Give Me the Number for 911

s = raw_input("Enter a number: ")
s = int(s)
t = int(not s)
print t