**Table of Content**

Python has two values of the `bool`

type, written `True`

and `False`

. These are called logical values

or **Boolean values**, named after 19th century mathematician George Boole.

In [1]:

```
True # show the value
```

Out[1]:

In [2]:

```
type(True) # check the type name
```

Out[2]:

In [3]:

```
False # show the value
```

Out[3]:

In [4]:

```
type(False) # check the type name
```

Out[4]:

In [5]:

```
true
```

In [6]:

```
false
```

We have seen arithmetic operators that produce as output numerical values. Today, we'll see **relational operators** that

produce as output **Boolean values**. Relational operators are used to compare two values.

Below try to first guess the values before running the cell.

In [7]:

```
3 < 5
```

Out[7]:

In [8]:

```
3 < 2
```

Out[8]:

In [9]:

```
3 > 2
```

Out[9]:

In [10]:

```
5 == 5
```

Out[10]:

In [11]:

```
5 >= 5
```

Out[11]:

In [12]:

```
6 <= 5
```

Out[12]:

**Note:** `==`

is pronounced *"equals"* and `!=`

is pronounced *"not equals"*. This is why we distinguish the pronunciation

of the single equal sign = as "gets", which is assignment and nothing to do with mathematical equality!

Relational operators can also be used to compare strings (in dictionary order).

In [13]:

```
'bat' < 'cat'
```

Out[13]:

In [14]:

```
'bat' < 'ant'
```

Out[14]:

In [15]:

```
'bat' == 'bat'
```

Out[15]:

In [16]:

```
'bat' < 'bath'
```

Out[16]:

In [17]:

```
'Cat' < 'bat'
```

Out[17]:

**EXPLANATION:** How does this comparison of string values work? Python starts by comparing the first character of each string to one another. For example "b" with "c". Because the computer doesn't know anything about letters, it converts everything into numbers. Each character has a numerical code that is summarized in this table of ASCII codes. In Python, we can look up the ASCII code via the Python built-in function `ord`

:

In [18]:

```
print(ord('a'), ord('b'), ord('c'))
```

As you can see, the value for 'b', 98, is smaller than the value for 'c', 99, thus, `'b' < 'c'`

. Once two unequal characters are found, Python stops comparing the other characters, because there is no point in continuing. However, if characters are the same, like in 'bat' and 'bath', the comparisons continue until the point in which something that differs is found. In this case, there is an extra 't', making 'bath' greater in value than 'bat'.

**Uppercase vs. Lowercase:** Counterintuitively, it turns out, the upppercase letters are internally represented with smaller numbers than lowercase letters. See the ASCII table and the examples below:

In [19]:

```
print(ord('A'), ord('a'))
```

In [20]:

```
print(ord('B'), ord('b'))
```

This explains why the world 'Cat' is smaller than the word 'cat'.

There are three logical operators: `not`

, `and`

, `or`

, which are applied on expressions that are already evaluated as boolean values.

`not`

¶**not** *expression* evaluates to the opposite of the truth value of *expression*

In [21]:

```
not (3 > 5) # parentheses not necessary - relational operators have higher precedence
```

Out[21]:

In [22]:

```
not (3 == 3)
```

Out[22]:

`and`

¶*exp1* **and** *exp2* evaluates to `True`

iff **both** *exp1* and *exp2* evaluate to `True`

.

In [23]:

```
True and True
```

Out[23]:

In [24]:

```
True and False
```

Out[24]:

In [25]:

```
(3 < 5) and ('bat' < 'ant')
```

Out[25]:

In [26]:

```
(3 < 5) and ('bat' < 'cat')
```

Out[26]:

`or`

¶*exp1* **or** *exp2* evaluates to `True`

iff **at least one** of *exp1* and *exp2* evaluate to `True`

.

In [27]:

```
True or True
```

Out[27]:

In [28]:

```
True or False
```

Out[28]:

In [29]:

```
(3 > 5) or ('bat' < 'cat')
```

Out[29]:

In [30]:

```
(3 > 5) or ('bat' < 'ant')
```

Out[30]:

You can assign booleans to variables just like any other value. The variables below represent whether I like particular genres of music.

In [31]:

```
# change these as you like to experiment
pop = False
rap = True
hiphop = True
```

In [32]:

```
not pop
```

Out[32]:

In [33]:

```
pop and rap
```

Out[33]:

In [34]:

```
rap and hiphop
```

Out[34]:

In [35]:

```
pop or hiphop
```

Out[35]:

In [36]:

```
hiphop or rap
```

Out[36]:

What are the order of operations here? Do we compute the operator `and`

first or `or`

?

In [37]:

```
rap or hiphop and pop
```

Out[37]:

**and** takes precendence over **or**, so the above expression is the same as

In [38]:

```
rap or (hiphop and pop)
```

Out[38]:

but not the same as

In [39]:

```
(rap or hiphop) and pop
```

Out[39]:

In [40]:

```
def doILikeMissyElliott(rap, hiphop, pop):
return rap or hiphop and pop
```

In [41]:

```
doILikeMissyElliott(False, True, True)
```

Out[41]:

In [42]:

```
doILikeMissyElliott(True, True, False)
```

Out[42]:

In [43]:

```
doILikeMissyElliott(False, False, True)
```

Out[43]:

Usually, the function body will contain a complex expression combining relational and logical expressions, as the following examples show:

In [44]:

```
def isFaculty(s):
return (s == 'Eni' or s == 'Carolyn'
or s == 'Andy' or s == 'Lyn'
or s == 'Sohie' or s == 'Peter')
```

In [45]:

```
isFaculty('Carolyn')
```

Out[45]:

In [46]:

```
isFaculty("andy")
```

Out[46]:

**Note:** Explain the result of the last cell. Is that what you expected?

**Expressing intervals of numbers:** We can combine relational expressions to create intervals of numbers that fulfill certain criteria. Below is a predicate that checks if a value is within a given interval of numbers.

In [47]:

```
def isBetween(n, lo, hi):
"""determines if n is between lo and hi"""
return (lo <= n) and (n <= hi)
```

**More fun with Math:** Is a number divisible by a factor? Is it even?

In [48]:

```
def isDivisibleBy(num, factor):
"""determines if num is divisible by factor"""
return (num % factor) == 0 # notice the remainder operator
def isEven(n):
"""determines if n is even"""
return isDivisibleBy(n, 2)
print('Is 3774 divisible by 11?', isDivisibleBy(3774, 11))
print('Is 473 even?', isEven(473))
```

Is n a prime integer less than 100? In the solution below, notice how we split the long expression across mutliple lines for readability, and wrap all multiline expressions in parentheses.

In [49]:

```
# Version with continuation characters for multiline expressions
def isSmallPrime(n):
return (isinstance(n, int) # is n an integer?
and (n > 1) and (n < 100) # is n between 1 and 100?
and (n==2 or n==3 or n==5 or n==7 # is n 2, 3, 5, or 7?
or not (isDivisibleBy(n,2) #is n divisible by 2, 3, 5, or 7?
or isDivisibleBy(n,3)
or isDivisibleBy(n,5)
or isDivisibleBy(n,7))))
```

In [50]:

```
isSmallPrime(23)
```

Out[50]:

In [51]:

```
isSmallPrime(42)
```

Out[51]:

When multiline expressions are **not** wrapped in parentheses, you **must** use the special **continuation character** \ at the end of each line. No character other than a newline can come after the \.

In [52]:

```
# Version with continuation characters for some multiline expressions
def isSmallPrime(n):
return isinstance(n, int) \
and (n > 1) and (n < 100) \
and (n==2 or n==3 or n==5 or n==7 # No need for \ because of parentheses
or not (isDivisibleBy(n,2)
or isDivisibleBy(n,3)
or isDivisibleBy(n,5)
or isDivisibleBy(n,7)))
```

The version below is an alternative solutions that uses De Morgan's laws.

In [53]:

```
# alternate version using De Morgan's laws
def isSmallPrime2(n):
return (isinstance(n, int)
and (n > 1) and (n < 100) # is n between 1 and 100?
and (n==2 or n==3 or n==5 or n==7 # is n 2, 3, 5, or 7?
or (not isDivisibleBy(n,2) #is n divisible by 2, 3, 5, or 7?
and not isDivisibleBy(n,3)
and not isDivisibleBy(n,5)
and not isDivisibleBy(n,7))))
```

Write the following four predicates.

**Remember**: a predicate is simply a function that returns a Boolean value.

`isFreezing`

: is the temperature in Fahrenheit at or below freezing?

`isLongName`

: is a name longer than 20 characters?

`isVowel`

: is a character a vowel?

`startsWithVowel`

: does a string start with a vowel?

**Do not use conditional statements to solve these problems. Only relational or logical expressions!**

In [54]:

```
# 1. isFreezing
# Your code here
def isFreezing(temp):
return temp <= 32
```

In [55]:

```
isFreezing(10)
```

Out[55]:

In [56]:

```
isFreezing(75)
```

Out[56]:

In [57]:

```
# 2. isLongName
# Your code here
def isLongName(name):
return len(name) > 20
```

In [58]:

```
isLongName('Wellesley')
```

Out[58]:

In [59]:

```
isLongName('Llanfairpwllgwyngyllgogerychwyrndrobwllllantysiliogogogoch')
```

Out[59]:

(Above is the name of a village in Wales; you can hear it pronounced in this video.)

In [60]:

```
# 3. isVowel
# There are multiple possible solutions for this problem
# Your code here
def isVowel(letter):
return letter.lower() in 'aeiou'
```

In [61]:

```
isVowel('e')
```

Out[61]:

In [62]:

```
isVowel('U')
```

Out[62]:

In [63]:

```
isVowel('b')
```

Out[63]:

**Note**: if s is a string, s[0] returns the first character of s.

In [64]:

```
# 4. startsWithVowel
# Your code here
def startsWithVowel(word):
return isVowel(word[0])
```

In [65]:

```
startsWithVowel('Esmeralda')
```

Out[65]:

In [66]:

```
startsWithVowel('bravery')
```

Out[66]:

Write an expression that uses `1`

, `5`

, and `<`

to produce the value `True`

, and store the result in the variable `x`

In [67]:

```
# Your code here:
x = 1 < 5
```

In [68]:

```
assert x == True
print('Success!')
```

Write an expression that uses `1`

, `5`

, and `<`

to produce the value `False`

and store the result in the variable `y`

In [69]:

```
# Your code here:
y = 5 < 1
```

In [70]:

```
assert y == False
print('Success!')
```

Write an expression that uses `True`

and `not`

to produce the value `False`

and store the result in the variable `nope`

In [71]:

```
# Your code here:
nope = not True
```

In [72]:

```
assert nope == False
print('Success!')
```

Write an expression that uses `1`

, `5`

, `not`

, `==`

to produce the value `True`

and store the result in the variable `z`

In [73]:

```
# Your code here:
z = not 1 == 5
```

In [74]:

```
assert z == True
print('Success!')
```

Write an expression that uses two boolean values and the operator `and`

to produce the value `True`

and store the result in the variable `sure`

In [75]:

```
# Your code here:
sure = True and True
```

In [76]:

```
assert sure == True
print('Success!')
```

Write an expression that uses two boolean values and the operator `or`

to produce the value `True`

and store the result in the variable `whyNot`

In [77]:

```
# Your code here:
whyNot = True or False
```

In [78]:

```
assert whyNot == True
print('Success!')
```

Write an expression that uses two boolean values and the operator `and`

to produce the value `False`

and store the result in the variable `becauseISaidSo`

In [79]:

```
# Your code here:
becauseISaidSo = True and False
```

In [80]:

```
assert becauseISaidSo == False
print('Success!')
```

Write an expression that uses two boolean values and the operator `or`

to produce the value `False`

and store the result in the variable `fairEnough`

In [81]:

```
# Your code here:
fairEnough = False or False
```