Paper 2 Programming Fundamentals Answers
These answers correspond to Paper 2 Programming Fundamentals Drills.
Verification note: every model code block in this file has been executed locally after the 2026-07-10 revision.
Answer 1: Grade Function
def grade_from_mark(mark):
if mark >= 75:
return "A"
if mark >= 60:
return "B"
if mark >= 50:
return "C"
return "U"
print(grade_from_mark(75))
print(grade_from_mark(74))
print(grade_from_mark(49))Precondition: mark is an integer from 0 to 100 inclusive, so extra validation is not required in this question.
Expected output:
A
B
UMark points:
- uses a function with parameter
mark; - checks the highest boundary first;
- uses
>=so boundary values are included; - returns the correct grade string for each range;
- returns
"U"for marks below50; - shows the required test outputs.
Common weak answer:
- checking
mark >= 50first, which would incorrectly return"C"for75.
Answer 2: List Average
def average(values):
if len(values) == 0:
return 0
return sum(values) / len(values)
print(average([6, 9, 12]))
print(average([]))Expected output:
9.0
0Mark points:
- checks for an empty list before dividing;
- calculates the sum of the list;
- divides by the number of values;
- returns the calculated mean;
- shows both required test outputs.
Common weak answer:
- dividing by
len(values)before checking for an empty list, causing division by zero.
Answer 3: 2D List Row Totals
def row_totals(grid):
totals = []
for row in grid:
row_total = 0
for value in row:
row_total = row_total + value
totals.append(row_total)
return totals
print(row_totals([[3, 4, 5], [10, 0, 2], [7, 8, 9]]))Expected output:
[12, 12, 24]Mark points:
- creates a result list;
- loops through each row;
- totals values within the current row;
- appends one total per row;
- returns the result list;
- produces the correct row totals;
- shows the required output.
Common weak answer:
- returning one grand total instead of a separate total for each row.
Answer 4: String Vowel Count
def count_vowels(text):
count = 0
for character in text.lower():
if character in "aeiou":
count = count + 1
return count
print(count_vowels("A quiet room"))
print(count_vowels("SKY"))Expected output:
6
0Mark points:
- initialises a counter;
- handles uppercase and lowercase input;
- loops through every character;
- checks membership in
aeiou; - increments only for vowels;
- shows both required outputs.
Common weak answer:
- checking only lowercase vowels without converting the input, so
"A"would be missed.
Answer 5: Recursive Factorial
def factorial(n):
if n == 0 or n == 1:
return 1
return n * factorial(n - 1)
print(factorial(0))
print(factorial(1))
print(factorial(5))Expected output:
1
1
120Mark points:
- defines a recursive function;
- includes a base case for
0or1; - returns
1in the base case; - calls
factorial(n - 1)in the recursive case; - multiplies by
n; - produces correct output for base cases;
- produces correct output for the recursive case
factorial(5).
Common weak answer:
- missing the base case, causing infinite recursion.
Answer 6: Read Lines
def count_at_least(filename, threshold):
count = 0
with open(filename, "r") as file:
for line in file:
mark = int(line.strip())
if mark >= threshold:
count = count + 1
return count
print(count_at_least("marks.txt", 60))Expected output:
2Mark points:
- opens the file for reading;
- loops through all lines;
- strips newline characters;
- converts each mark to integer;
- compares with
thresholdusing>=; - counts matching records;
- returns and prints the correct count.
Common weak answer:
- comparing marks as strings instead of integers.
Answer 7: Write Results
def write_passes(records, filename):
with open(filename, "w") as file:
for record in records:
name = record[0]
mark = record[1]
if mark >= 50:
file.write(name + "," + str(mark) + "\n")
records = [["Asha", 74], ["Ben", 49], ["Chen", 82], ["Divya", 50]]
write_passes(records, "passes.txt")
with open("passes.txt", "r") as file:
print(file.read(), end="")Expected output:
Asha,74
Chen,82
Divya,50Mark points:
- opens the output file in write mode;
- loops through all records;
- selects records with marks at least
50; - converts the mark to string before writing;
- writes one record per line in the requested format;
- prints the resulting file contents.
Common weak answer:
- omitting the newline, causing all records to appear on one line.
Answer 8: Student Code Normalisation
def normalise_student_code(code):
cleaned = code.strip().upper()
if len(cleaned) < 2:
return "INVALID"
if cleaned[0] != "S":
return "INVALID"
for character in cleaned[1:]:
if character < "0" or character > "9":
return "INVALID"
return cleaned
print(normalise_student_code(" s1042 "))
print(normalise_student_code("T204"))
print(normalise_student_code(" s10a "))Expected output:
S1042
INVALID
INVALIDMark points:
- defines
normalise_student_code(code); - strips leading and trailing spaces;
- converts letters to uppercase;
- checks that the cleaned code is long enough before indexing;
- checks that the first character is
"S"; - checks every remaining character is a digit;
- returns
"INVALID"for invalid inputs; - produces all required outputs.
Common weak answer:
- checking only the first character and forgetting to reject a code such as
"S10A".
Answer 9: First Index Below Limit
def first_below(values, limit):
for index in range(len(values)):
if values[index] < limit:
return index
return -1
print(first_below([80, 75, 49, 60], 50))
print(first_below([55, 60], 50))
print(first_below([49], 50))Expected output:
2
-1
0Mark points:
- defines
first_below(values, limit); - loops through valid indexes of the list;
- accesses each value using the current index;
- compares each value with
limitusing<; - returns the index immediately when the first matching value is found;
- returns
-1when no value is below the limit; - handles the boundary case where the first item matches;
- produces all required outputs.
Common weak answer:
- returning the value
49instead of its index2.
Answer 10: Function Decomposition
def is_pass(mark):
return mark >= 50
def student_result(name, mark):
if is_pass(mark):
return name + ": pass"
return name + ": resit"
print(student_result("Asha", 50))
print(student_result("Ben", 49))
print(student_result("Chen", 75))Expected output:
Asha: pass
Ben: resit
Chen: passThe second function calls the Boolean helper is_pass(mark) instead of repeating the pass/fail condition independently.
Mark points:
- defines
is_pass(mark); - returns a Boolean result from
is_pass; - uses the boundary condition
mark >= 50; - defines
student_result(name, mark); - calls
is_pass(mark)insidestudent_result; - returns the correct
"name: pass"or"name: resit"string; - produces all required outputs, including the boundary case
50.
Common weak answer:
- duplicating the condition inside
student_resultand never calling the helper function.