JB

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, then x is perfectly divisible by y.

      • Extracting Right-most Digits:- x ext{ % } 10 yields the right-most digit of x (in base 10).

        • x ext{ % } 100 yields the last two digits of x.

  • 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 or False.

  • Equality Operator (==): Compares two operands and produces True if they are equal, and False otherwise.- Examples:

    python 5 == 5 # True 5 == 6 # False

  • True and False: These are special values belonging to the bool type, not strings.

  • Relational Operators: Used to compare two operands.- x ext{ == } y: x is equal to y

    • x ext{ != } y: x is not equal to y

    • x ext{ > } y: x is greater than y

    • x ext{ < } y: x is less than y

    • x ext{ >= } y: x is greater than or equal to y

    • x ext{ <= } y: x is less than or equal to y

  • 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 to True because the ASCII value of 'a' (97) is greater than 'Z' (90).

Logical Operators

  • There are three logical operators: and, or, and not. They combine boolean expressions.

  • and operator: Returns True if both operands are True.- Example: x > 0 and x < 10 is True only if x is between 0 and 10 (exclusive).

  • or operator: Returns True if either or both operands are True.- Example: n % 2 == 0 or n % 3 == 0 is True if n is divisible by 2, by 3, or by both.

  • not operator: Negates a boolean expression; it returns True if the operand is False, and False if the operand is True.- Example: not (x > y) is True if x > y is False, which means x 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 the condition.

    • If the condition is True, the indented statement(s) (the body) run.

    • If the condition is False, 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 the pass 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 is True, the first set of statements (branch 1) runs.

  • If the condition is False, 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 to True will have its corresponding branch executed, and then the entire conditional statement ends.

  • Important: Even if multiple conditions are True, only the first True 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 preceding if or elif conditions are True.

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 of countdown(3):1. n=3: Prints 3, calls countdown(2).
    2. n=2: Prints 2, calls countdown(1).
    3. n=1: Prints 1, calls countdown(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 string n times. python def print_n(s, n): if n &lt;= 0: return # Base case: exit function print(s) print_n(s, n-1) # Recursive call- Base Case: The if n &lt;= 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 by countdown frames for n=3, n=2, n=1, and n=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 a ValueError 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.