Decisions#

In the last lecture, we saw that computer programs are recipes that tell the computer what to do. Programs must be precise and correct. So far, the programs we have written have done the same things every time we run them. Here, we will see how to write code that executes only when certain conditions are true. Decisions control the flow of the program.

From individual decisions, we can build decision trees, which are an important part of expert systems. Expert systems are AI systems that have existed for a long time, and are a kind of knowledge-based systems.

The if statement#

Say we write a program that needs to decide whether someone is a minor or adult. We can do this with an if test based on the age:

age = int(input("What is the defendant's age?"))
if age < 18:
    print('Defendant is a minor')
Defendant is a minor

We can also include an alternate path, else:

if age < 18:
    print('Defendant is a minor')
else:
    print('Defendant is an adult')
print('done')
Defendant is a minor
done

This code can be illustrated with a flowchart: if illustration

Indentation#

The code “inside” the if statement is called a block. Each line in a block must be indented the same amount, the convention is four spaces. The else statement starts another block.

if age < 18:
    print('Defendant is a minor')
    print('Parental consent is required')
    # call a function to handle parental consent
else:
    print('Defendant is an adult')
    # call a function to handle consent
print('done')

Relational Operators#

if statements start with a condition that is often some kind of comparison. These comparisons use relational operators. Python has six relational operators:

Relational operator

Meaning

>

Greater than

>=

Greater than or equal

<

Less than

<=

Less than or equal

==

Equal

!=

Not equal

Note that the relational operator for equality is ==, a double equal sign. This is because a single = is used for assignment.

We must take care to choose the correct operator to avoid off by one errors. What’s wrong here?

if age > 18:
    print('Defendant is an adult')

Comparing strings#

We can also compare strings. Strings are sequences of characters. To be equal, strings must have:

  1. The same length

  2. The same characters, position by position

We can order strings alphabetically (lexicographically) by using relational operators. “Larger” strings come later in the alphabet:

print('Eve' < 'Jada')
True

Nested if statements#

An if statement can contain other, nested if statements. Nested if statements are also called decision trees. Their flowcharts have a tree structure.

if age < 18:
    print('Defendant is a minor')
    if age < 15:
        print('Below age of criminal responsibility')
else:
    print('Defendant is an adult')

Multiple Alternatives#

Many nested if statements can be simplified to elif statements, short for “else if”.

if age < 13:
    print('child')
elif age < 20:
    print('teenager')
elif age > 70:
    print('senior citizen')
else:
    print('adult')

Warning

Make sure the order of the if tests is correct. If we had tested for teenagers before children, children would have been classified as teenagers.

Boolean Values#

The result of a condition is called a Boolean value. Boolean values are either True or False. We can assign Boolean values to variables to be used later.

For example, we might want to go through some steps to get consent for processing of personal data. We start out with no consent:

consent_given = False
# do steps to get consent

Boolean Operators#

We can combine multiple conditions into larger Boolean expressions using Boolean Operators.

valid_consent = age >= 18 and consent_given

Note

The Boolean variable consent_given already has a truth value, so we don’t need to use a relational operator. Never use relational operators with True or False. Don’t write if consent_given == True:, but simply if consent_given:

There are three boolean operators: and, or, and not. As we saw above, and expression with and is True if both sides are True. An expression with or is True if either side is True:

parental_consent = False
valid_consent = parental_consent or age >= 18

Or we could combine all three conditions:

valid_consent = parental_consent or (age >= 18 and consent_given)
print(valid_consent)
False

The Boolean operator not inverts the value of the expression that follows it. Try to use variable names that results in code that reads naturally:

if not parental_consent:
    print('No parental consent given')
No parental consent given

De Morgan’s Laws#

Some Boolean expressions can be simplified:

  • not (A or B) is the same as not A and not B

  • not (A and B) is the same as not A or not B

The last two expressions might be easier to read. Notice that when we remove the parenthesis, we “flip” the operator from and to or, or the other way around. We distribute the not operators.

These two rules are known as De Morgan’s Laws after the mathematician Augustus De Morgan.

married = True
divorced = False

if not (married and divorced):
    print('do something')

if not married or not divorced:
    print('equivalent with the above')
do something
equivalent with the above

Analyzing Strings#

We saw above that the standard relational operators can be used with strings. But Python also has the in (and not in) operators for checking if a string contains another substring.

Note

You can copy text from code cells by clicking the Copy button in the top right corner of the cells. The Copy button appears when you place the mouse pointer inside a cell or touch it on a touchscreen.

text = '''Agreement

1. Introduction: The lender is Bank Cred AS (990 099 909). 
The borrower is the person or persons (borrower and co-applicant) who apply
for and are granted a loan with this Agreement.
Payment: The credit is paid to the account number the Borrower states.

2. Repayment: The Borrower repays the loan amount at fixed term amounts, including 
interest and term fees (i.e. annuity loans) as specified in the Loan Agreement.

3. Late Payment Interest: In the event of late payment, late payment interest accrues
at the interest rate determined in accordance with the Act relating to Interest on
Overdue Payments.

Signature Place/date: Oslo, 08.08.2021
For Bank Cred AS Bank adviser Maria Wilson
Name: Christopher Thomson Borrower'''
if 'signature' in text:
    print('found')
else:
    print('not found')
not found

Why isn’t the string signature found in the text?

We should normalize the text to lowercase to find all possible variants.

print('signature' in text.lower())
True

Python has several methods for analyzing strings. Here are a few examples, there are many more.

print(text.startswith('Agreement'))
print(text.count('loan'))
position = text.find('Signature')
print(position)
print(text[position:])
print('text is numeric:', text.isnumeric())
print('text isalpha:', text.isalpha())
True
3
620
Signature Place/date: Oslo, 08.08.2021
For Bank Cred AS Bank adviser Maria Wilson
Name: Christopher Thomson Borrower
text is numeric: False
text isalpha: False

Input Validation#

When a program accepts input from the user, we need to check that the input is valid, that is within the range of expected answers. This is especially important with web applications. if tests are a simple way of validating the input.

marital_status = input('Please input marital status, "single" or "married": ')

# normalize response by lowercaseing it
marital_status = marital_status.lower()

if marital_status == 'single':
    print('calling function to handle single people')
elif marital_status == 'married':
    print('calling function to handle married people')
else:
    print('error: invalid marital status')

Constants#

Until now, we have used numbers like the age of majority directly in our code, hard coding the number 18 in our if tests. This is bad practice. The age of majority has changed before and it might change again. If this happens, we will need to track down every occurrence of it in our code, and change them to the new age. To avoid this, we can make a constant once, and use that. By convention, constant are in all capital letters.

AGE_OF_MAJORITY = 18

if age < AGE_OF_MAJORITY:
    print('child')