📝 ICS4U — Final Exam

Cumulative summative evaluation across all 5 units
⏱️ 3 hours · /100 marks · Worth up to 30% of final grade
Instructions. All code must be syntactically valid Python 3. Show all work, including trace tables and reasoning where requested. Use proper style: meaningful names, comments where useful. Write code by hand — you may not run it. No internet, no AI tools, no calculators except a basic four-function calculator.
K/U
/25
Thinking
/25
Communication
/25
Application
/25
Part A — Knowledge & Understanding [25 marks]
1. [3]
Unit 1 · Tracing
What is the output of:
x = 5 y = 0 for i in range(4): y = y + x x = x - 1 print(x, y)
2. [2]
Unit 1 · Types
Give the value AND the type of each:
  1. 3 / 2
  2. 3 // 2
  3. "a" * 3
  4. bool(0)
3. [3]
Unit 2 · Lists
Given a = [10, 20, 30, 40, 50], write the value of:
  1. a[1:4]
  2. sum(a) / len(a)
  3. a[::-1]
4. [3]
Unit 2 · Dictionaries
Trace and give the final value of d:
d = {} for ch in "MISSISSIPPI": d[ch] = d.get(ch, 0) + 1 print(d)
5. [3]
Unit 3 · Recursion
What does f(4) return?
def f(n): if n <= 1: return n return f(n-1) + f(n-2)
6. [3]
Unit 3 · Scope
What is printed?
total = 0 def add(x): total = x return total print(add(5)) print(total)
7. [3]
Unit 4 · Big-O
State the worst-case Big-O complexity of:
  1. Linear search
  2. Binary search
  3. Bubble sort
  4. Merge sort
  5. Insertion sort on a sorted list (best case)
8. [2]
Unit 5 · Testing
Define "regression test" in one sentence. Why is regression testing important when fixing a bug?
9. [3]
Unit 5 · SDLC
Place the following SDLC steps in correct order: Maintenance, Design, Implementation, Requirements gathering, Testing, Deployment.
Part B — Thinking & Investigation [25 marks]
10. [6]
Unit 4 · Sort trace
Trace insertion sort (ascending) on [7, 3, 9, 2, 8, 1]. Show the list after EACH iteration of the outer loop.
11. [6]
Unit 4 · Binary search
Trace binary search for the value 57 in the sorted list:
[3, 8, 14, 21, 28, 35, 42, 49, 57, 64, 71]
Show lo, hi, and mid at each step. Conclude by stating the index returned.
12. [6]
Unit 3 · Recursion design
Write a recursive function palindrome(s) that returns True if a string is the same forwards and backwards (case-sensitive). You may not use any loops or string-reversal built-ins. Then trace palindrome("level").
13. [7]
Units 1+2+3 · Debugging
Find & fix the bugs. The function below should return the average of even numbers in a list. There are three bugs. (a) Provide the corrected function. (b) Briefly state what each bug was.
def avg_evens(nums): total = 0 count = 1 for n in range(nums): if n % 2 = 0: total + n count += 1 return total / count
Part C — Communication [25 marks]
14. [6]
Units 2+3 · OOP design
Design a Python class Library with attributes and methods to manage a small library. Include AT LEAST: an __init__, an add_book(title) method, a borrow(title) method (returns True on success or False if not on shelf), a return_book(title) method, and a docstring describing the class. Use sensible data structures (e.g., a dict mapping title to count, or two sets for "on shelf" and "borrowed"). Comment thoroughly.
15. [6]
Unit 4 · Algorithm comparison
Compare merge sort and quicksort in 3-5 sentences each. Address: time complexity (best/average/worst), space, stability, and one situation where you'd prefer each.
16. [7]
Unit 5 · Ethics essay
Write a 200-300 word response: "Should developers of AI systems be legally responsible for harms caused by those systems?" Take a clear position. Your response must reference: (a) at least one concrete real-world AI harm, (b) at least one stakeholder beyond the developer, and (c) a comparison to liability in another industry (e.g., automotive, pharmaceutical).
17. [6]
Unit 5 · Code review
Below is a peer's submission. Write a constructive code review: identify at least five distinct quality issues (style, correctness, maintainability, naming, comments, etc.) and suggest a fix for each.
def x(L,n): r=[] for i in range(0,len(L)): if L[i]>n:r.append(L[i]) return r
Part D — Application [25 marks]
18. [6]
Units 1+2 · File I/O + dict
A file orders.csv contains lines of the form customer,item,price, e.g.:
Ada,book,25.50 Bob,pen,3.00 Ada,pen,3.00 Cay,book,25.50
Write a complete program that reads the file and prints the total spending per customer, sorted by name. The output should look like:
Ada: $28.50 Bob: $3.00 Cay: $25.50
19. [6]
Unit 4 · Sort + search
A school keeps student GPAs in a list of dicts: [{"name":..., "gpa":...}, ...]. Write a function top_n(students, n) that returns the names of the n students with the HIGHEST GPAs, in descending order. Then briefly justify your choice of algorithm and state its complexity.
20. [6]
Units 3+4 · Recursion + complexity
Naive Fibonacci is O(2ⁿ). Rewrite Fibonacci iteratively (with a single loop) so it runs in O(n) time and O(1) extra space. Then briefly explain why your version is faster than the recursive one.
21. [7]
All units · Capstone-style
A teacher wants a small grade-tracking program. Required:
  1. A class Course with attributes name (str) and marks (list of floats).
  2. A method add_mark(m) that appends a mark in the range 0–100 (raise ValueError otherwise).
  3. A method average() returning the mean.
  4. A method highest() returning the maximum mark (or None if no marks yet).
  5. A free function rank(courses) that takes a list of Course objects and returns them sorted by average descending.
Write the complete code with docstrings and at least 3 inline comments. Then briefly describe how you would unit-test it.
📖 Complete Answer Key (click to reveal)

Part A — K/U

Q1. Loop runs i=0..3. Iter 0: y=5,x=4. Iter 1: y=9,x=3. Iter 2: y=12,x=2. Iter 3: y=14,x=1. Output: 1 14.

Q2. (1) 1.5 (float). (2) 1 (int). (3) "aaa" (str). (4) False (bool).

Q3. (1) [20, 30, 40]. (2) 30.0. (3) [50, 40, 30, 20, 10].

Q4. {'M': 1, 'I': 4, 'S': 4, 'P': 2}.

Q5. f(4) = f(3)+f(2) = (f(2)+f(1)) + (f(1)+f(0)) = ((f(1)+f(0))+1) + (1+0) = ((1+0)+1)+1 = 3. (This is Fibonacci F(4) where F(0)=0, F(1)=1.)

Q6. Inside add, total = x creates a local total shadowing the global. Output: 5 then 0.

Q7. O(n), O(log n), O(n²), O(n log n), O(n).

Q8. A regression test re-runs old test cases after a code change to confirm previously-correct behaviour still works. Important because fixes can introduce new bugs in unrelated places.

Q9. Requirements gathering → Design → Implementation → Testing → Deployment → Maintenance.

Part B — Thinking

Q10. [7,3,9,2,8,1] → [3,7,9,2,8,1] → [3,7,9,2,8,1] → [2,3,7,9,8,1] → [2,3,7,8,9,1] → [1,2,3,7,8,9].

Q11. Step 1: lo=0, hi=10, mid=5 (35 < 57; lo=6). Step 2: lo=6, hi=10, mid=8 (57 = 57). Return index 8.

Q12.

def palindrome(s): if len(s) <= 1: return True if s[0] != s[-1]: return False return palindrome(s[1:-1])
Trace: palindrome("level") → first/last 'l'/'l' match → palindrome("eve") → 'e'/'e' match → palindrome("v") → True.

Q13. Bugs: (1) range(nums) should be nums (iterating list, not int). (2) = should be ==. (3) total + n doesn't store anything — should be total += n. (4) Counting bug: count = 1 initially makes the average wrong; should be 0 (and protect division-by-zero).

def avg_evens(nums): total, count = 0, 0 for n in nums: if n % 2 == 0: total += n count += 1 return total / count if count else 0

Part C — Communication

Q14. Sample:

class Library: """A simple library that tracks copies of books and borrowing.""" def __init__(self): self.shelf = {} # title -> count available def add_book(self, title): self.shelf[title] = self.shelf.get(title, 0) + 1 def borrow(self, title): if self.shelf.get(title, 0) > 0: self.shelf[title] -= 1 return True return False def return_book(self, title): self.shelf[title] = self.shelf.get(title, 0) + 1

Q15. Merge sort: O(n log n) best/avg/worst, O(n) extra space, stable. Quicksort: O(n log n) avg, O(n²) worst (bad pivot), O(log n) recursion stack, generally not stable, but in-place. Prefer merge sort when stability matters or worst-case guarantees are needed (e.g., real-time). Prefer quicksort when memory is tight and average performance dominates (most general-purpose libraries).

Q16. Free response. Award marks for: (a) clear position; (b) named real-world harm (e.g., bias in COMPAS, autonomous-vehicle deaths, generative-AI defamation); (c) named stakeholder (data subject, regulator, end-user); (d) industry comparison (e.g., automotive recall law, FDA pharmaceutical liability).

Q17. Sample issues: (1) Function name x is uninformative → rename filter_above. (2) Parameter L is uninformative → rename lst or numbers. (3) Inconsistent / unusual indentation (1 then 2 spaces) → PEP 8: 4 spaces. (4) range(0, len(L)) can be for x in lst: — iterate values directly. (5) Missing docstring. (6) Single-line if/append is hard to read. (7) No tests/comments.

Part D — Application

Q18. Sample:

spending = {} with open("orders.csv") as f: for line in f: c, _, p = line.strip().split(",") spending[c] = spending.get(c, 0) + float(p) for c in sorted(spending): print(f"{c}: ${spending[c]:.2f}")

Q19.

def top_n(students, n): return [s["name"] for s in sorted(students, key=lambda s: -s["gpa"])[:n]]
Sorting is O(n log n). With n much smaller than total, a partial-selection (heap-based) approach in O(N log n) could be faster for large datasets, but the O(N log N) full sort is clearer and adequate for class-sized lists.

Q20.

def fib(n): a, b = 0, 1 for _ in range(n): a, b = b, a + b return a
The naive recursive solution recomputes the same sub-problems exponentially many times. The iterative version computes each Fibonacci value once in O(n) and uses only two variables of state.

Q21. Sample:

class Course: """A course with a list of marks and helpers to compute statistics.""" def __init__(self, name): self.name = name self.marks = [] # list of floats in [0, 100] def add_mark(self, m): if not 0 <= m <= 100: raise ValueError(f"mark {m} out of range") self.marks.append(m) def average(self): # avoid div-by-zero on a fresh course return sum(self.marks) / len(self.marks) if self.marks else 0.0 def highest(self): return max(self.marks) if self.marks else None def rank(courses): """Return courses sorted by average descending.""" return sorted(courses, key=lambda c: -c.average())
Testing: cover (a) empty course (average=0, highest=None), (b) full marks 0..100, (c) raise on -1 and 101, (d) rank with 3 courses of differing averages.

Final Exam Rubric (per category)

LevelDescription%
4Thorough, insightful, demonstrates a high degree of effectiveness80–100%
3Considerable effectiveness — provincial standard70–79%
2Some effectiveness60–69%
1Limited effectiveness50–59%
RInsufficient — has not demonstrated the required knowledge or skills<50%