Lesson 3: Conditionals and Recursion
Floor Division and Modulus
The primary topic of this lesson is the
if
statement, but first, we introduce two new operators: floor division and modulus.Floor Division Operator (
//
):- Divides two numbers and rounds the result down to the nearest integer.Example: To convert minutes to hours for a movie runtime (e.g., 105 minutes):
python minutes = 105 hours = minutes // 60 print(hours) # Output: 1
Modulus Operator (
%
):- Divides two numbers and returns the remainder of the division.Example: To find the remaining minutes after floor division:
python minutes = 105 remainder = minutes % 60 print(remainder) # Output: 45
Practical Uses of Modulus:- Checking Divisibility: If
x ext{ % } y
is zero, thenx
is perfectly divisible byy
.Extracting Right-most Digits:-
x ext{ % } 10
yields the right-most digit ofx
(in base 10).x ext{ % } 100
yields the last two digits ofx
.
Python 2 vs. Python 3 Division: In Python 2, the
/
operator performed floor division if both operands were integers; otherwise, it performed floating-point division. In Python 3,/
always performs floating-point division, and//
is explicitly for floor division.
Boolean Expressions
A boolean expression is an expression that evaluates to either
True
orFalse
.Equality Operator (
==
): Compares two operands and producesTrue
if they are equal, andFalse
otherwise.- Examples:python 5 == 5 # True 5 == 6 # False
True
andFalse
: These are special values belonging to thebool
type, not strings.Relational Operators: Used to compare two operands.-
x ext{ == } y
:x
is equal toy
x ext{ != } y
:x
is not equal toy
x ext{ > } y
:x
is greater thany
x ext{ < } y
:x
is less thany
x ext{ >= } y
:x
is greater than or equal toy
x ext{ <= } y
:x
is less than or equal toy
Common Error: Using a single equal sign (
=
) (assignment operator) instead of the double equal sign (==
) (relational operator).ASCII Codes: Computers understand numbers. ASCII code is the numerical representation of a character (e.g., 'a', '@'). Comparisons between characters are based on their ASCII values.- Example:
"a" > "Z"
evaluates toTrue
because the ASCII value of 'a' (97) is greater than 'Z' (90).
Logical Operators
There are three logical operators:
and
,or
, andnot
. They combine boolean expressions.and
operator: ReturnsTrue
if both operands areTrue
.- Example:x > 0 and x < 10
isTrue
only ifx
is between 0 and 10 (exclusive).or
operator: ReturnsTrue
if either or both operands areTrue
.- Example:n % 2 == 0 or n % 3 == 0
isTrue
ifn
is divisible by 2, by 3, or by both.not
operator: Negates a boolean expression; it returnsTrue
if the operand isFalse
, andFalse
if the operand isTrue
.- Example:not (x > y)
isTrue
ifx > y
isFalse
, which meansx ext{ <= } y
.Python's Flexibility: Python is not strict about requiring boolean expressions for logical operators; any non-zero number is interpreted as
True
. This can be useful but also confusing, so it's generally best to avoid it unless you fully understand its implications.
Conditional Execution (if
Statements)
Conditional statements allow programs to check conditions and alter their behavior accordingly, making them useful.
Simplest Form:
if
statement:- Syntax:python if condition: # indented statements (body)
The boolean expression after
if
is thecondition
.If the
condition
isTrue
, the indented statement(s) (the body) run.If the
condition
isFalse
, nothing happens.
Compound Statements:
if
statements are compound statements, consisting of a header (the line ending with a colon:
) and an indented body.Empty Body (
pass
statement): If a body is required but no action is needed yet, use thepass
statement as a placeholder.
python if x < 0: pass # TODO: handle negative values
Alternative Execution (if-else
Statements)
Allows for two execution paths based on a condition.
Syntax:
python if condition: # branch 1 statements else: # branch 2 statements
If the
condition
isTrue
, the first set of statements (branch 1) runs.If the
condition
isFalse
, the second set of statements (branch 2) runs.Exactly one of the alternatives (branches) will always run.- Example: Checking if a number is even or odd.
python if x % 2 == 0: print('x is even') else: print('x is odd')
Chained Conditionals (if-elif-else
Statements)
Used when there are more than two possibilities, creating multiple branches.
Syntax:
python if condition1: # branch 1 elif condition2: # branch 2 elif condition3: # branch 3 else: # final branch (optional)
elif
is an abbreviation for "else if".Execution Flow: Conditions are checked in order. The first
condition
that evaluates toTrue
will have its correspondingbranch
executed, and then the entire conditional statement ends.Important: Even if multiple conditions are
True
, only the firstTrue
branch runs.The
else
clause is optional and, if present, must be at the very end. It serves as a catch-all if none of the precedingif
orelif
conditions areTrue
.
Nested Conditionals
A conditional statement can be placed inside one of the branches of another conditional statement.
Example: Rewriting a three-way comparison using nesting.
python if x == y: print('x and y are equal') else: if x < y: print('x is less than y') else: print('x is greater than y')
Disadvantage: While structurally apparent due to indentation, deeply nested conditionals quickly become difficult to read and manage.
Simplification with Logical Operators: Logical operators (
and
,or
) can often simplify nested conditionals.- Original Nested:python if 0 < x: if x < 10: print('x is a positive single-digit number.')
Simplified with
and
:
python if 0 < x and x < 10: print('x is a positive single-digit number.')
Python's Concise Option: For chained comparisons, Python allows a more compact syntax.
python if 0 < x < 10: print('x is a positive single-digit number.')
Recursion
Recursion occurs when a function calls itself. It is a powerful programming technique.
Example:
countdown
function:python def countdown(n): if n <= 0: print('Blastoff!') else: print(n) countdown(n-1) # Recursive call
- Behavior ofcountdown(3)
:1. n=3: Prints 3, callscountdown(2)
.
2. n=2: Prints 2, callscountdown(1)
.
3. n=1: Prints 1, callscountdown(0)
.
4. n=0: Prints "Blastoff!", then returns.
5.countdown(1)
returns.
6.countdown(2)
returns.
7.countdown(3)
returns.<!-- -->
Output:
3 2 1 Blastoff!
Recursive Function: A function that calls itself.
Recursion (Process): The process of executing a recursive function.
Example:
print_n
function: Prints a stringn
times.python def print_n(s, n): if n <= 0: return # Base case: exit function print(s) print_n(s, n-1) # Recursive call
- Base Case: Theif n <= 0: return
line is the base case. It causes the function to exit immediately without a recursive call, preventing infinite recursion.The total number of lines output is
1 + (n - 1) = n
.
When to Use Recursion: For simple cases,
for
loops are often easier. However, recursion is essential for solving problems that are difficult to express iteratively.
Stack Diagrams for Recursive Functions
A stack diagram helps visualize the state of a program during function calls.
Each time a function is called, Python creates a new frame on the call stack. This frame holds the function's local variables and parameters.
For recursive functions, multiple frames for the same function can exist on the stack simultaneously, each with different values for local variables/parameters.- Example for
countdown(3)
: The stack would show__main__
at the bottom, followed bycountdown
frames forn=3
,n=2
,n=1
, andn=0
at the top.Base Case in Stack Diagrams: The recursive call that does not make further recursive calls is known as the base case. When the base case completes, its frame is removed, and the stack unwinds, returning control to the calling frame below it.
Infinite Recursion
If a recursive function never reaches its base case, it continues to make recursive calls indefinitely, leading to infinite recursion.
Behavior: An infinitely recursive program will typically not run forever in most programming environments.
Python's Response: Python reports a
RuntimeError: Maximum recursion depth exceeded
when the call stack grows too large (default limit is usually 1000 frames).Debugging Infinite Recursion:- Verify that your recursive function has a defined base case.
Ensure that the function's arguments are changing correctly with each recursive call, guaranteeing that the base case will eventually be reached.
Examine the traceback for the chain of recursive calls leading to the error.
Keyboard Input
Most programs need to interact with the user, taking input to vary their behavior.
input()
function:- A built-in Python function that pauses program execution and waits for the user to type something and press Enter/Return.It returns the user's input as a string.
(Python 2 vs. 3): In Python 2, the equivalent function was
raw_input()
.Example:
python text = input() # User types: What are you waiting for? print(text) # Output: What are you waiting for?
Prompts: It's good practice to provide a prompt to the user, instructing them what to type.-
input()
can take a string argument, which serves as the prompt.The
\n
(newline character) at the end of a prompt string causes the user's input to appear on the next line.Example:
python name = input('What...is your name?\n') # User types: Arthur, King of the Britons! print(name) # Output: Arthur, King of the Britons!
Type Conversion: User input is always a string. If you expect a different type (e.g., integer, float), you must explicitly convert it.- Example (converting to integer):
python prompt = 'What...is the airspeed velocity of an unladen swallow?\n' speed_str = input(prompt) # User types: 42 speed_int = int(speed_str) print(speed_int) # Output: 42 (an integer)
Handling Invalid Input: If the user types input that cannot be converted to the desired type, a
ValueError
will occur.- Example:int('What do you mean, an African or a European swallow?')
will raise aValueError
because the string is not a valid integer literal.
Debugging
Error messages (tracebacks) are crucial for debugging, though they can initially seem overwhelming.
Key Information in Error Messages:- Kind of error: The type of exception (e.g.,
IndentationError
,ValueError
,RuntimeError
).Where it occurred: The file name, line number, and function call stack.
Syntax Errors: Usually easier to locate.- Whitespace Errors: Particularly tricky because spaces and tabs are invisible. An
IndentationError
might point to the line where the incorrect indentation was detected, which might not be the line where the actual error began.Runtime Errors: Can be more subtle.- Example: Signal-to-Noise Ratio (SNRdb) Calculation Error:- Formula:
SNR*{db} = 10 imes ext{log}*{10}( rac{P*{signal}}{P*{noise}})
.
- Problem Code Snippet:`python import math signal_power = 9 noise_power = 10 ratio = signal_power // noise_power # Floor division here decibels = 10 * math.log10(ratio) print(decibels) ` - **Error:** This code produces `ValueError: math domain error` on the line calling `math.log10(ratio)`. - **Misleading Message:** The error message points to line 5 (`decibels = ...`), but the real issue is in line 4 (`ratio = ...`). - **Root Cause:** `signal_power // noise_power` (floor division) evaluates to 0, and ` ext{log}_{10}(0)` is undefined, causing the `ValueError`. - **Solution:** Use floating-point division (`/`) instead of floor division (`//`) for `ratio`. <!-- -->
Debugging Strategy: Read error messages carefully, print intermediate values to inspect program state, but remember that the indicated error location might be where the problem was discovered, not necessarily where the initial error occurred.