B2.1.1 Construct, trace programs with variables (AO3)

B2.1.1_1 Data types: Boolean, char, decimal, integer, string

Example Program (Python)

# Declare variables with appropriate data types
student_name = "Alice" # String: Stores textual data
is_passing = True # Boolean: True/False for logical conditions
grade1 = 85 # Integer: Whole number for grades
grade2 = 90
weight = 75.5 # Decimal (float): For precise numerical values
initial = 'A' # Char (string of length 1): Single character
# Calculate average grade
average = (grade1 + grade2) / 2
# Output results
print(f"Student: {student_name}, Initial: {initial}")
print(f"Average Grade: {average}")
print(f"Passing: {is_passing}")

Tracing the Program

  • Initialize: student_name = "Alice", is_passing = True, grade1 = 85, grade2 = 90, weight = 75.5, initial = 'A'
  • Calculate: average = (85 + 90) / 2 = 87.5
  • Output: Prints "Student: Alice, Initial: A", "Average Grade: 87.5", "Passing: True"

Boolean

  • Stores True or False for logical conditions
  • Example: is_passing = grade >= 60

Char

  • Single character (in Python, a string of length 1)
  • Example: initial = 'A'

Decimal (Float)

  • Represents numbers with decimal points
  • Example: weight = 75.5 for precise measurements

Integer

  • Whole numbers for counts or discrete values
  • Example: grade1 = 85

String

  • Text data for names, descriptions, etc.
  • Example: student_name = "Alice"

Purpose

  • Selecting appropriate data types ensures accurate data storage, manipulation, and program functionality
  • Tracing verifies program logic by tracking variable changes and confirming expected outputs

B2.1.2 Construct programs for substring manipulation (AO3)

B2.1.2_1 Identify, extract, alter, concatenate, replace substrings

Identify

  • Locate specific substrings using methods like find() or index()
  • Example: text = "Hello World"; pos = text.find("World") returns 6

Extract

  • Retrieve a portion using slicing or substring methods
  • Example: text = "Hello World"; substring = text[6:11] extracts "World"

Alter

  • Modify parts by creating a new string with changes
  • Example: text = "hello"; text = text.upper() results in "HELLO"

Concatenate

  • Combine strings using + operator or join() method
  • Example: first = "Hello"; last = "World"; full = first + " " + last

Replace

  • Substitute a substring using replace() method
  • Example: text = "Hello World"; new_text = text.replace("World", "Python")

Example Program (Python)

# Program to manipulate a string
text = "Welcome to IB Computer Science"
# Identify: Find position of substring
pos = text.find("Computer")
print(f"Position of 'Computer': {pos}") # Output: 11
# Extract: Get substring
substring = text[11:19]
print(f"Extracted substring: {substring}") # Output: Computer
# Alter: Convert to uppercase
upper_text = substring.upper()
print(f"Uppercase substring: {upper_text}") # Output: COMPUTER
# Concatenate: Add prefix
new_text = "Subject: " + text
print(f"Concatenated: {new_text}") # Output: Subject: Welcome to IB Computer Science
# Replace: Change substring
replaced_text = text.replace("Computer Science", "Programming")
print(f"Replaced text: {replaced_text}") # Output: Welcome to IB Programming

Construction and Tracing

  • Construct: Write code to perform substring operations based on requirements
  • Trace: Step through the code, tracking variable values:
    • text = "Welcome to IB Computer Science"
    • pos = 11 (from find("Computer"))
    • substring = "Computer" (from slicing [11:19])
    • upper_text = "COMPUTER" (from upper())
    • new_text = "Subject: Welcome to IB Computer Science" (from concatenation)
    • replaced_text = "Welcome to IB Programming" (from replace())

Use Case

  • A program processing email addresses to extract usernames (e.g., user@domain.com → user)
  • Concatenate with a greeting, and replace domains for standardization
  • Ensuring correct manipulation through tracing

B2.1.3 Describe exception handling (AO2)

B2.1.3_1 Failure points: unexpected inputs, resource unavailability, logic errors

Unexpected Inputs

  • Occurs when user data does not match expected format or type
  • Example: Entering "abc" when expecting an integer
  • Can cause crashes (e.g., ValueError) or incorrect behavior

Resource Unavailability

  • Failure due to inaccessible resources (files, databases, network)
  • Example: Attempting to open a non-existent file
  • Results in FileNotFoundError, disrupting execution

Logic Errors

  • Errors in program logic (division by zero, out-of-bounds access)
  • Example: Dividing by a variable that equals zero
  • Causes runtime errors or incorrect outputs

B2.1.3_2 Role in program development

Purpose

  • Exception handling ensures programs gracefully handle errors
  • Improves robustness and user experience
  • Prevents crashes by catching and managing errors
  • Allows programs to recover or fail gracefully

Benefits in Development

  • Enhances reliability by anticipating and addressing potential failure points
  • Improves debugging by providing clear error messages and controlled error handling
  • Supports user-friendly applications by informing users of issues without abrupt termination

Use in Testing

  • Helps developers identify and handle edge cases during testing
  • Ensures comprehensive error management
  • Example: Testing a calculator program for division by zero

B2.1.3_3 Constructs: try/catch (Java), try/except (Python), finally block

Try/Catch (Java)

try {
    int result = 10 / 0; // Potential ZeroDivisionError
} catch (ArithmeticException e) {
    System.out.println("Error: Division by zero");
}
  • Catches specific exceptions to prevent crashes

Try/Except (Python)

try:
    result = 10 / 0 # Potential ZeroDivisionError
except ZeroDivisionError:
    print("Error: Division by zero")
  • Manages runtime errors, allowing program continuation

Finally Block (Java and Python)

try:
    file = open("data.txt", "r")
    content = file.read()
except FileNotFoundError:
    print("Error: File not found")
finally:
    file.close() # Ensures file is closed
  • Executes code regardless of whether an exception occurs
  • Often used for cleanup tasks (e.g., closing files)
  • Ensures resources are properly released, preventing leaks

Use Case

  • A Python program reading user input for a calculation uses try/except to handle invalid inputs
  • Finally block logs the operation's completion
  • Ensures robustness and proper resource management

B2.1.4 Construct, use debugging techniques (AO3)

B2.1.4_1 Techniques: trace tables, breakpoints, print statements, step-by-step execution

Trace Tables

  • Table tracking variable values at each step
  • Identifies logic errors or unexpected behavior
  • Example: For a loop calculating sum:
total = 0
for i in range(1, 4):
    total += i
print(total)
Step i total
1 1 1
2 2 3
3 3 6

Breakpoints

  • Pause execution at specific lines to inspect state
  • Used with debuggers (e.g., Python's pdb, IDEs)
  • Example: Setting breakpoint at total += i to check values
  • Pinpoints where errors occur

Print Statements

  • Insert print() to output variable values
  • Quick monitoring without debugger
  • Example:
total = 0
for i in range(1, 4):
    total += i
    print(f"i={i}, total={total}")
  • Output: i=1, total=1, i=2, total=3, i=3, total=6

Step-by-Step Execution

  • Execute code line-by-line using debugger
  • Observe behavior and variable changes in real-time
  • Tools: pdb, VS Code, PyCharm debuggers
  • Identifies exact lines where errors occur

Construction and Use

# Example: Debug a program to find the maximum number
numbers = [4, 7, 2, 9, 1]
max_num = numbers[0]
for num in numbers:
    print(f"Checking num={num}, max_num={max_num}") # Print statement
    if num > max_num:
        max_num = num
print(f"Maximum: {max_num}")
Step num max_num
1 4 4
2 7 7
3 2 7
4 9 9
5 1 9

B2.2.1 Compare static vs dynamic data structures (AO3)

B2.2.1_1 Memory allocation, resizing mechanisms

Static Data Structures

Memory Allocation
  • Memory allocated at compile time
  • Fixed size that cannot change during runtime
  • Example: numbers = [0] * 5 creates a Python list with 5 elements
Resizing Mechanism
  • Cannot be resized dynamically
  • Size is set during declaration
  • To change size, a new structure must be created and data copied

Dynamic Data Structures

Memory Allocation
  • Memory allocated at runtime
  • Flexible size adjustments as needed
  • Example: numbers = [1, 2, 3]; numbers.append(4) dynamically adds an element
Resizing Mechanism
  • Automatically resizes when capacity exceeded
  • Allocates new memory and copies data
  • Example: Python lists double capacity when full

B2.2.1_2 Pros, cons: speed, memory, flexibility

Aspect Static Data Structures Dynamic Data Structures
Speed Faster access (O(1)) due to contiguous memory and fixed size Slower due to resizing overhead (O(n) in worst case)
Memory More efficient, no overhead for resizing Higher overhead for resizing and extra capacity
Flexibility Inflexible, fixed size Highly flexible, grows/shrinks as needed
Use Case Fixed number of student grades (exactly 10) Tracking user inputs in a chat application

Static Pros

  • Faster access due to contiguous memory
  • Memory-efficient with no overhead

Static Cons

  • Inflexible size cannot change
  • Wasted space if not fully utilized

Dynamic Pros

  • Flexible size adjusts as needed
  • Easy to use with automatic memory management

Dynamic Cons

  • Slower due to resizing overhead
  • Higher memory usage for extra capacity

B2.2.2 Construct programs with arrays, lists (AO3)

B2.2.2_1 1D/2D arrays, ArrayLists (Java)

1D Arrays

  • Fixed-size, contiguous collection of elements
  • Accessed by index
# 1D array using Python list
grades = [85, 90, 78, 92]
grades[2] = 80 # Update element
print(grades[0]) # Access: 85
  • Use: Store sequences like student grades

2D Arrays

  • Matrix-like structure with rows and columns
  • Accessed by two indices
# 2D array using nested lists
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
matrix[1][2] = 10 # Update element
print(matrix[0][1]) # Access: 2
  • Use: Represent grids, tables, or seating charts

ArrayLists (Java)

  • Dynamic, resizable list in Java
  • Part of java.util package
import java.util.ArrayList;
// Initialize ArrayList
ArrayList<Integer> scores = new ArrayList<>();
scores.add(85); // Add element
scores.add(90);
scores.set(1, 95); // Update element
System.out.println(scores.get(0)); // Access: 85
  • Use: Store dynamic collections like user inputs

B2.2.2_2 1D/2D lists (Python)

1D Lists

  • Python's built-in dynamic lists
  • Resizable and can store mixed data types
# 1D list
names = ["Alice", "Bob", "Charlie"]
names.append("David") # Add element
names[1] = "Beth" # Update element
print(names[0]) # Access: Alice
  • Use: Store dynamic sequences like customer names

2D Lists

  • Nested lists representing a grid or table
  • Dynamic resizing capabilities
# 2D list
grid = [[0, 1, 0], [1, 0, 1], [0, 1, 0]]
grid[1][1] = 2 # Update element
print(grid[0][2]) # Access: 0
grid.append([1, 1, 1]) # Add new row
  • Use: Represent dynamic matrices like game boards

B2.2.2_3 Add, remove, traverse dynamic list elements

Add Elements

  • Append or insert elements to expand size
students = ["Alice", "Bob"]
students.append("Charlie") # Add to end
students.insert(1, "Beth") # Add at index 1
print(students) # ['Alice', 'Beth', 'Bob', 'Charlie']

Remove Elements

  • Remove by value, index, or clear entire list
students = ["Alice", "Beth", "Bob", "Charlie"]
students.remove("Beth") # Remove by value
students.pop(1) # Remove by index (Bob)
print(students) # ['Alice', 'Charlie']

Traverse Elements

  • Iterate through elements to process or analyze
scores = [85, 90, 78, 92]
for score in scores:
    print(f"Score: {score}")
# Access by index
for i in range(len(scores)):
    scores[i] += 5 # Add 5 to each
print(scores) # [90, 95, 83, 97]

Use Case

  • A program managing a student roster:
    • Adds new students (append)
    • Removes dropouts (remove)
    • Traverses the list to calculate average grades or print names
  • Ensures dynamic updates and efficient data processing

B2.2.3 Explain stack (LIFO) (AO2)

B2.2.3_1 Operations: push, pop, peek, isEmpty

Push

  • Adds element to the top of the stack
  • Example: Adding a plate to a stack of plates
stack = []
stack.append(5) # Push 5 onto stack

Pop

  • Removes and returns the top element
  • Example: Taking the top plate off
top = stack.pop() # Removes and returns 5

Peek

  • Returns top element without removing it
  • Example: Checking the top plate
top = stack[-1] if stack else None # Views top element

isEmpty

  • Checks if stack is empty
  • Returns True if no elements remain
is_empty = len(stack) == 0 # Returns True if empty

B2.2.3_2 Impact on performance, memory

Performance

  • Push/Pop: O(1) time complexity
  • Operations occur at the top (constant-time access)
  • Peek/isEmpty: O(1) time complexity
  • Only access top element or check length
  • Example: Fast for function call management

Memory

  • Uses contiguous or dynamic memory
  • Minimal overhead for simple operations
  • Dynamic resizing may cause occasional O(n) operations
  • Example: Function call stack uses small, fixed space per element

Trade-offs

  • Efficient for LIFO operations but limited for random access or searching (O(n))
  • Memory-efficient for sequential data but may waste space if underused

B2.2.3_3 Appropriate stack use

Function Call Stack

  • Manages function calls and returns
  • Tracks execution context
  • Example: Python's call stack in recursion

Undo/Redo Operations

  • Tracks actions in reverse order
  • Example: Text editor undo feature

Expression Evaluation

  • Processes expressions or validates syntax
  • Example: Checking balanced parentheses

Backtracking Algorithms

  • Supports algorithms like depth-first search
  • Example: Solving a maze by storing visited paths

Python Example

# Stack for checking balanced parentheses
def is_balanced(expression):
    stack = []
    for char in expression:
        if char == '(':
            stack.append(char) # Push
        elif char == ')':
            if len(stack) == 0: # Check isEmpty
                return False
            stack.pop() # Pop
    return len(stack) == 0
print(is_balanced("(a + b)")) # Output: True
print(is_balanced("(a + b))")) # Output: False

Appropriateness

  • Ideal for LIFO access, reversing actions, or managing nested operations
  • Inappropriate for random access or frequent searching

B2.2.4 Explain queue (FIFO) (AO2)

B2.2.4_1 Operations: enqueue, dequeue, front, isEmpty

Enqueue

  • Adds element to the back of the queue
  • Example: Adding a customer to a ticket line
queue = []
queue.append(10) # Enqueue 10

Dequeue

  • Removes and returns the front element
  • Example: Serving the first customer
front = queue.pop(0) # Dequeue first element (10)

Front

  • Returns the front element without removing
  • Example: Checking the next customer
front = queue[0] if queue else None # View front element

isEmpty

  • Checks if queue is empty
  • Returns True if no elements remain
is_empty = len(queue) == 0 # Returns True if empty

B2.2.4_2 Impact on performance, memory

Performance

  • Enqueue: O(1) using dynamic list (append)
  • Dequeue: O(n) in basic Python list due to shifting
  • Optimized implementations (collections.deque) achieve O(1)
  • Front/isEmpty: O(1) time complexity
# Optimized implementation
from collections import deque
queue = deque()
queue.append(10) # O(1)
queue.popleft() # O(1)

Memory

  • Uses contiguous or dynamic memory
  • Overhead for resizing in dynamic implementations
  • Efficient for sequential processing
  • May waste memory if pre-allocated capacity is underused
  • Example: Print job queue uses minimal space per job

Trade-offs

  • Fast for FIFO operations but inefficient for random access (O(n))
  • Optimized implementations like deque improve performance

B2.2.4_3 Appropriate queue use

Task Scheduling

  • Manages tasks in arrival order
  • Example: CPU process scheduling

Event Handling

  • Processes events/messages in order received
  • Example: Server queueing user requests

Breadth-First Search

  • Explores nodes level by level
  • Example: Finding shortest path in a maze

Buffering

  • Handles data streams
  • Example: Video player queueing frames

Python Example

from collections import deque
# Queue for processing print jobs
def process_print_jobs(jobs):
    queue = deque()
    for job in jobs:
        queue.append(job) # Enqueue job
    while len(queue) > 0: # Check isEmpty
        current_job = queue.popleft() # Dequeue front job
        print(f"Processing: {current_job}")
    return len(queue) == 0
print_jobs = ["Doc1.pdf", "Doc2.pdf", "Doc3.pdf"]
print(process_print_jobs(print_jobs)) # Processes each job, returns True

Appropriateness

  • Ideal for FIFO scenarios, processing tasks in order of arrival
  • Inappropriate for LIFO or random access, where stacks or arrays are better

B2.4.5 Construct, trace recursive algorithms (HL) (AO3)

B2.4.5_1 Simple, non-branching recursive algorithms

Constructing Recursive Algorithms

  • Solve problems by calling themselves with smaller inputs
  • Consist of:
    • Base case: Terminates recursion
    • Recursive case: Reduces problem size
  • Non-branching: Makes a single recursive call per iteration

Factorial Example

def factorial(n):
    # Base case
    if n == 0 or n == 1:
        return 1
    # Recursive case
    return n * factorial(n - 1)
# Computes n! (e.g., 5! = 5 × 4 × 3 × 2 × 1 = 120)

Tracing Recursive Algorithms

  • Follow each recursive call, tracking values until base case
  • Then compute final output by unwinding the call stack
Trace for factorial(4):
  • Call 1: factorial(4) → 4 × factorial(3)
  • Call 2: factorial(3) → 3 × factorial(2)
  • Call 3: factorial(2) → 2 × factorial(1)
  • Call 4: factorial(1) → Base case, returns 1
  • Unwind:
    • factorial(2) → 2 × 1 = 2
    • factorial(3) → 3 × 2 = 6
    • factorial(4) → 4 × 6 = 24
  • Result: factorial(4) returns 24

Sum of Numbers Example

def sum_to_n(n):
    # Base case
    if n <= 1:
        return n
    # Recursive case
    return n + sum_to_n(n - 1)
# Computes sum from 1 to n (e.g., sum_to_n(4) = 1+2+3+4 = 10)
Trace for sum_to_n(4):
  • Call 1: sum_to_n(4) → 4 + sum_to_n(3)
  • Call 2: sum_to_n(3) → 3 + sum_to_n(2)
  • Call 3: sum_to_n(2) → 2 + sum_to_n(1)
  • Call 4: sum_to_n(1) → Base case, returns 1
  • Unwind:
    • sum_to_n(2) → 2 + 1 = 3
    • sum_to_n(3) → 3 + 3 = 6
    • sum_to_n(4) → 4 + 6 = 10
  • Result: sum_to_n(4) returns 10

Key Considerations

  • Ensure a clear base case to prevent infinite recursion
  • Non-branching algorithms are simpler to trace and construct
  • Example Use Case: Calculating power of a number (x^n)
def power(x, n):
    if n == 0: # Base case
        return 1
    return x * power(x, n - 1) # Recursive case
# Trace for power(2, 3):
# power(2, 3) → 2 * power(2, 2)
# power(2, 2) → 2 * power(2, 1)
# power(2, 1) → 2 * power(2, 0)
# power(2, 0) → 1
# Unwind: 2 * 1 = 2, 2 * 2 = 4, 2 * 4 = 8
# Result: 8

B2.5.1 Construct file-processing code (AO3)

B2.5.1_1 Manipulate text files

Description

  • Writing code to read from, write to, or modify text files
  • Enables data persistence and processing
  • Purpose: Store, retrieve, or update data for tasks like logging, configuration, or analysis
  • Example: Reading student names from a file, updating grades, and saving results

B2.5.1_2 Open files in read, write, append modes

Read Mode ('r')

  • Opens file for reading
  • Raises error if file doesn't exist
  • Example: Reading student grades from text file

Write Mode ('w')

  • Opens file for writing
  • Overwrites if exists or creates new file
  • Example: Writing processed data to a file

Append Mode ('a')

  • Opens file to add data at the end
  • Preserves existing content
  • Example: Adding new log entries without overwriting

Python Code Example

# Example: Manipulating a text file with different modes
# Write mode: Create or overwrite a file
with open('students.txt', 'w') as file:
    file.write("Alice,85\nBob,90\n") # Write initial data
# Append mode: Add new data
with open('students.txt', 'a') as file:
    file.write("Charlie,78\n") # Append new student
# Read mode: Read and print file contents
with open('students.txt', 'r') as file:
    content = file.read()
    print("File Contents:", content)

B2.5.1_3 Read, write, append, close files

Read

  • Retrieves data using methods like read(), readline(), or readlines()
  • Example: file.read() reads entire file as string
  • file.readlines() reads lines into a list

Write

  • Writes data to file in 'w' mode
  • Overwrites existing content
  • Example: file.write("data") writes a string

Append

  • Adds data to end in 'a' mode
  • Preserves existing content
  • Example: file.write("new data\n") appends a line

Close

  • Closes file to free system resources
  • Automatically handled using 'with' statement
  • Example: with open('file.txt', 'r') as file: ensures closure

Python Code Example

# Read, write, append example
# Write initial data
with open('grades.txt', 'w') as file:
    file.write("Alice: 85\n")
# Append additional data
with open('grades.txt', 'a') as file:
    file.write("Bob: 90\n")
# Read and process file
with open('grades.txt', 'r') as file:
    lines = file.readlines() # Read all lines into a list
    for line in lines:
        name, grade = line.strip().split(': ')
        grade = int(grade) + 5 # Increase grade by 5
        print(f"Updated: {name}, {grade}")

B2.5.1_4 Java classes: Scanner, FileWriter, BufferedReader

Scanner

  • Java class (java.util.Scanner) for reading data
  • Parses input into tokens
import java.util.Scanner;
import java.io.File;
File file = new File("students.txt");
Scanner scanner = new Scanner(file);
while (scanner.hasNextLine()) {
    System.out.println(scanner.nextLine());
}
scanner.close();

FileWriter

  • Java class (java.io.FileWriter) for writing
  • Writes character data to files
import java.io.FileWriter;
FileWriter writer = new FileWriter("students.txt");
writer.write("Alice,85\n");
writer.close(); // Explicitly close

BufferedReader

  • Java class for efficient reading
  • Buffers input for better performance
import java.io.BufferedReader;
import java.io.FileReader;
BufferedReader reader = new BufferedReader(
    new FileReader("students.txt"));
String line;
while ((line = reader.readLine()) != null) {
    System.out.println(line);
}
reader.close();

B2.5.1_5 Python functions: open(), read(), readline(), write(), close()

open()

  • Opens file in specified mode
  • Returns a file object
  • Example: file = open('data.txt', 'r')

read()

  • Reads entire file as string
  • Example: content = file.read()

readline()

  • Reads a single line
  • Returns empty string at end
  • Example: line = file.readline()

write()

  • Writes string to file
  • Example: file.write("Hello\n")

close()

  • Closes file, freeing resources
  • Used without 'with' statement
  • Example: file.close()

Example Program

# Process a CSV file with student data
# Write student data
with open('students.csv', 'w') as file:
    file.write("Name,Grade\nAlice,85\nBob,90\n")
# Read and manipulate data
with open('students.csv', 'r') as file:
    lines = file.readlines()[1:] # Skip header
    for line in lines:
        name, grade = line.strip().split(',')
        grade = int(grade) + 5 # Increase grade by 5
        print(f"Updated: {name}, {grade}")
# Append new student
with open('students.csv', 'a') as file:
    file.write("Charlie,78\n")

Use Case

  • A program managing a log file:
    • Writes new entries (write, append mode)
    • Reads and analyzes past entries (readlines)
    • Ensures proper closure (with or close)
  • Enables reliable file processing for tasks like tracking user activity or storing configuration data