Skip to content

Week 6 - Conditionals and Recursion

Conditionals and Recursion in Python

Objective

By the end of this lesson, you will understand and implement Python conditionals, logical operations, recursion, and error handling. You will be able to: - Apply the modulus operator and boolean expressions - Write programs using if, elif, and else statements - Utilize recursion effectively - Handle basic input and debugging in Python


Integer Division and the Modulus Operator

Key Concepts:

  • Integer Division //
  • Modulus Operator %

Explanation:

Integer division truncates the decimal part of a division, while the modulus operator returns the remainder of a division operation. These concepts can be useful for tasks like:

  • Converting time formats
  • Checking divisibility
  • Handling clock arithmetic

Code Example:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# Example: Converting minutes to hours and minutes
minutes = 145
hours = minutes // 60
remainder = minutes % 60
print(f'{hours} hours and {remainder} minutes')
# Alternatively
minutes = 145
hours, minutes = divmod(minutes, 60)  # Built-in Function, so faster!
print(f'{hours} hours and {remainder} minutes')

# Clock arithmetic example:
start_time = 11  # Start time is 11 AM
duration = 3     # Duration is 3 hours
end_time = (start_time + duration) % 12
print(f'End time: {end_time} PM')

Boolean Expressions and Logical Operators

Key Concepts:

  • Relational operators: ==, !=, <, >, <=, >=
  • Logical operators: and, or, not

Explanation:

Boolean expressions result in True or False. Combining these expressions with logical operators allows for more complex decision-making in programs.

Code Example:

1
2
3
4
# Checking multiple conditions using logical operators
x = 7
print(x > 0 and x < 10)  # True
print(x % 2 == 0 or x % 3 == 0)  # True (divisible by 3)

Boolean expressions that are complex can often be simplified via logic laws, for example:

Code Example:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# Consider the somewhat complex boolean logic below
def complex_logic(a, b, c):
    if not (a or b) and not c:
        return True
    else:
        return False

# Example use
result = complex_logic(True, False, False)
print("Result:", result)

DeMorgan’s law states:

  • not (A or B) is equivalent to not A and not B
  • not (A and B) is equivalent to not A or not B

Truth Table for not (A or B) and not A and not B

A B A or B not (A or B) not A not B not A and not B
True True True False False False False
True False True False False True False
False True True False True False False
False False False True True True True

As you can see, the columns for not (A or B) and not A and not B produce the same values, which proves the equivalence of the first DeMorgan’s law.


Truth Table for not (A and B) and not A or not B

A B A and B not (A and B) not A not B not A or not B
True True True False False False False
True False False True False True True
False True False True True False True
False False False True True True True

Here, the columns for not (A and B) and not A or not B also produce identical values, proving the second DeMorgan’s law.


So the above example becomes more clear written like so:

Code Example

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# Simplified logic using DeMorgan's law
def simplified_logic(a, b, c):
    if (not a and not b) and not c:
        return True
    else:
        return False

# Example use
result = simplified_logic(True, False, False)
print("Result after applying DeMorgan's law:", result)

Truth Table for Code

We will evaluate the function for all possible combinations of a, b, and c (True and False). Both the original and simplified logic should produce the same results.

a b c Original Expression (not (a or b) and not c) Simplified Expression (not a and not b and not c) Equivalent
True True True False False Yes
True True False False False Yes
True False True False False Yes
True False False False False Yes
False True True False False Yes
False True False False False Yes
False False True False False Yes
False False False True True Yes

As shown in the truth table, both the original and simplified logic yield the same results for all possible combinations of a, b, and c. This confirms that applying DeMorgan’s law results in equivalent expressions.


Conditional Statements (if, else, elif)

Key Concepts:

  • if, else, elif for branching logic

Explanation:

Conditional statements let programs decide which code to run based on specific conditions. Branching logic can include multiple possibilities using if, else, and elif.

Code Example:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
x = 5
if x % 2 == 0:
    print(f'{x} is even')
else:
    print(f'{x} is odd')

# Chained conditional with elif
if x < 0:
    print('x is negative')
elif x == 0:
    print('x is zero')
else:
    print('x is positive')

Nested Conditionals and Logical Simplification

Key Concepts:

  • Nested if statements
  • Simplifying conditionals with logical operators

Explanation:

You can nest conditionals inside other conditionals, but this can lead to confusing code. Logical operators like and and or can often simplify these nested conditionals.

Code Example:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# Nested conditionals
x = 7
if x > 0:
    if x < 10:
        print('x is a positive single-digit number')

# Simplified using and operator
if x > 0 and x < 10:
    print('x is a positive single-digit number')

# Simplified even farther with Pythonic syntax
if 0 < x < 10:
    print('x is a positive single-digit number')

Introduction to Recursion

Key Concepts:

  • Recursive functions
  • Base case and recursive case

Explanation:

A recursive function is a function that calls itself. It MUST have a base case to prevent infinite recursion. Recursion is useful for problems like countdowns, fractals, and some mathematical algorithms.

Code Example:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# Recursive countdown function
def countdown(n):
    if n <= 0:
        print('Blastoff!')
    else:
        print(n)
        countdown(n-1)

countdown(5)

# Recursive function to print a string multiple times
def print_n_times(string, n):
    if n > 0:
        print(string)
        print_n_times(string, n-1)

print_n_times('Hello', 3)

It’s worth noting that recursion is a powerful tool, BUT loops are more efficient.


Debugging Recursive Functions

Key Concepts:

  • Understanding recursion errors (e.g., infinite recursion)
  • Stack frames and base cases

Explanation:

Recursion without a proper base case leads to infinite recursion and crashes the program. Use print statements and stack diagrams to debug recursive functions. * Remember for Stack Diagrams there’s -> Python Tutor

Code Example:

1
2
3
4
5
6
7
8
# Stack diagram and recursion debugging
def recurse(n, s):
    if n == 0:
        print(s)
    else:
        recurse(n-1, n+s)

recurse(3, 0)  # Stack diagram shows how values change

Review of Handling User Input

Key Concepts:

  • input() function
  • Converting string input to integers, floats, etc.

Explanation:

You can prompt the user for input using input(). Always remember to handle the conversion of strings to numbers carefully to avoid errors.

Code Example:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
# Prompting for user input
name = input('What is your name? ')
print(f'Hello, {name}!')

# Handling integer input
while True:
    try:
        age = int(input('How old are you? '))
        print(f'{name}, you are {age} years old.')
        break
    except ValueError as ve:
        print("That's not a valid age. Please enter an integer!")

while True:
    try:
        height = float(input('How tall are you? '))
        print(f'{name} you are {height} feet tall.')
        break
    except ValueError as ve:
        print("That's not a valid height. Please enter float!")

Exercises and Practice

Practice Exercises:

  1. Exercise 1: Modulus Operator Write a program that checks whether a number is divisible by both 3 and 5.

  2. Exercise 2: Recursion
    Write a recursive function that prints the sum of all integers from 1 to n.

  3. Exercise 3: User Input with Error Handling
    Ask the user for two integers and divide them. Handle errors like division by zero.

Answers
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#!/usr/bin/env python3

def three_or_five_div(number):
    return number % 3 == 0 or number % 5 == 0

print(three_or_five_div(9))
print(three_or_five_div(10))
print(three_or_five_div(16))

def recursive_sum(n):
    if n == 1:
        return 1
    else:
        return n + recursive_sum(n - 1)  # Leap of faith!

print(recursive_sum(10))

def get_int_input(message):
    quit_message = " Q to cancel:"
    while True:
        try:
            n1 = input(message + quit_message).strip()
            if n1.upper() == 'Q':
                return None
            return int(n1)
        except ValueError as ve:
            print( "Invalid integer input" )

def int_without_div0_error():
    try:
        n2 = None if (n1 := get_int_input("Enter your first number")) is None else get_int_input("Enter your second number")
        # n1 = get_int_input("Enter your first number")
        # n2 = get_int_input("Enter your second number")
        return n1 / n2
    except ZeroDivisionError:
        print("The divisor can not be 0")
    except TypeError:
        print("You must enter 2 integers")

print(int_without_div0_error())