Fundamentals of Programming
Exam-focused AQA 7517, OCR H446 and WJEC/Eduqas revision for programming fundamentals: data types, control flow, subroutines, parameter passing, recursion and exception handling with step-by-step code traces.
Exam-focused AQA 7517, OCR H446 and WJEC/Eduqas revision for programming fundamentals: data types, control flow, subroutines, parameter passing, recursion and exception handling with step-by-step code traces.
Every exam board expects you to choose and justify a data type, not just name it. The core set is integer (whole numbers), real/float (numbers with a fractional part), Boolean (True/False), character (a single symbol) and string (a sequence of characters). AQA 4.1.1 adds date/time and records; OCR 2.2.1 expects you to know that a string is stored as a sequence of character codes.
Marks are commonly lost by justifying types with tautologies ('an integer is an integer'). Give a consequence-based justification: a price stored as an integer loses pence in arithmetic; a Boolean flag makes a condition readable (isValid rather than status == 1); a constant for VAT rate means one change updates the whole program and prevents accidental reassignment.
| Operation | Pseudocode | Python | Example |
|---|---|---|---|
| Integer division | 17 DIV 5 | 17 // 5 | 3 |
| Modulus (remainder) | 17 MOD 5 | 17 % 5 | 2 |
| Exponent | 2 ^ 10 | 2 ** 10 | 1024 |
| Casting | stringToInt('42') | int('42') | 42 |
DIV and MOD are heavily examined (AQA 4.1.3). Three standard idioms to memorise:
n MOD 2 == 0 is true when n is even.digit = n MOD 10 gives the last digit; n = n DIV 10 removes it. Repeating this loops through the digits of 9021 as 1, 2, 0, 9.slot = (slot + 1) MOD tableSize wraps around (used again in hash-table open addressing).Rounding is a separate operation from casting: int(3.9) truncates to 3, whereas round(3.9) gives 4. Exams reward stating which one an algorithm uses. Also remember operator precedence: in most languages MOD and DIV bind like multiplication, so 10 + 3 MOD 4 evaluates as 10 + (3 MOD 4) = 13, not (10 + 3) MOD 4.
Selection chooses between branches; iteration repeats. For revision, the marks live in the differences between constructs, because 'state one difference' questions are near-guaranteed (AQA 4.1.2, OCR 2.2.1, WJEC Unit 2.1).
The classic written question is: 'Explain the difference between a while loop and a do-while loop.' Model phrasing: a while loop tests its condition before executing the body and so may execute it zero times, whereas a do-while/repeat-until loop tests after the body and therefore always executes at least once; choose do-when the loop must run at least once (e.g. reading a menu choice).
The classic trace trap is a sentinel-controlled loop where the loop condition is accidentally inverted or the sentinel is included in processing. Practice with this pattern:
total = 0
num = inputInt()
while num != -1:
total = total + num
num = inputInt()
print(total)Dry run with inputs 4, 9, -1: pass 1 — num=4, not -1, total=4, read 9; pass 2 — num=9, total=13, read -1; pass 3 — condition fails, loop exits, output 13. The -1 is never added. A frequent error in candidates' answers is stopping the trace one iteration early and forgetting the final read that terminates the loop.
Nested selection inside iteration compounds trace difficulty. Keep a dry-run table with one column per variable and update it every time a value changes — examiners award method marks for a correct table even if the final answer is wrong. Also know break (leave the loop immediately) and continue (skip to the next iteration): OCR paper 2 has asked candidates to rewrite a loop that uses break as a condition-controlled loop without it.
A function returns exactly one value and is used inside an expression; a procedure performs an action and returns nothing (it may still change things through reference parameters or output). Benefits of subroutines — a 4-marker across all boards: code is written once and reused (maintainability), a large problem is decomposed into named subproblems (decomposition), each subroutine can be tested independently (unit testing), and local variables prevent accidental interference between parts of the program.
Parameter passing is the highest-frequency written topic in this section (OCR 2.2.1, AQA 4.1.2). By value, the actual argument's value is copied into the formal parameter, so assignment inside the subroutine is invisible to the caller. By reference, the parameter is an alias for the caller's variable, so changes persist. Trace both versions:
// version A — by value
procedure doubleA(x)
x = x * 2
n = 5
doubleA(n)
print(n) // outputs 5
// version B — by reference
procedure doubleB(byref x)
x = x * 2
n = 5
doubleB(n)
print(n) // outputs 10Step-by-step for version A: (1) n is created holding 5; (2) doubleA is called, the value 5 is copied into local parameter x; (3) x becomes 10 — this changes only the copy; (4) subroutine ends, x is destroyed; (5) n still holds 5. For version B the only difference is step 2–3: x is n, so writing 10 through x writes through to n. Practical rules worth quoting in essays: pass small scalars by value when the subroutine must not alter them; pass large structures (arrays, records) by reference to avoid copying costs — but then the subroutine can modify them, which is a documented side effect.
Scope: a variable declared inside a subroutine is local and destroyed on return; a global is visible everywhere. Two scope traps: (1) shadowing — a local parameter with the same name as a global hides the global inside that subroutine; (2) in Python, assigning to a global inside a function without a global declaration creates a new local instead. Exams ask you to argue why globals are poor style: any subroutine can change them, so the data flow of the program is not visible in the call structure, and debugging is harder.
Recursion (AQA 4.1.2, OCR 2.2.1): a subroutine that calls itself. It must have a base case (terminating condition) and a recursive case that moves towards it. Trace factorial(4) showing the stack:
function fact(n)
if n == 0 then
return 1
else
return n * fact(n - 1)Winding: fact(4) calls fact(3) calls fact(2) calls fact(1) calls fact(0). At depth 5 the base case fires and returns 1. Unwinding: fact(0)=1, so fact(1)=1*1=1, fact(2)=2*1=2, fact(3)=3*2=6, fact(4)=4*6=24. Each call pushes a new stack frame holding its own n, which is why unbounded recursion causes a stack overflow — the classic exam link between recursion and the call stack. Any recursive routine can also be written iteratively (using a loop and an explicit accumulator); iterative versions avoid stack-depth limits and are usually faster, but recursive versions often mirror the problem definition more clearly.
Validation and exception handling are different defences and exams test that distinction (OCR 2.2.1; WJEC Unit 2.1). Validation checks that input is reasonable and in the right format before processing: range check (0 <= mark <= 100), length check, format/pattern check, type check, presence check. Exception handling deals with errors that validation cannot prevent — a file being missing, a network dropping, division by a value that turned out to be zero.
A typical full-credit Python pattern:
def read_mark():
while True:
try:
mark = int(input('Enter mark (0-100): '))
if 0 <= mark <= 100:
return mark
print('Out of range - try again')
except ValueError:
print('Not a whole number - try again')
finally:
pass # runs after every attempt, success or failureWalking through the blocks: the try block wraps code that may raise; except ValueError catches the specific exception when the cast fails; a general except catches everything else but should be avoided because it hides real bugs; else (not shown) runs only when no exception occurred; finally runs regardless — its exam purpose is cleanup such as closing files or connections. The with open(...) construct is the idiomatic cleanup for files: the file is closed automatically even if an exception occurs mid-read, which is the exact point to make for a 'why use with?' question.
File I/O modes: read (r) — file must exist; write (w) — creates a new file or truncates an existing one to empty; append (a) — writes after existing content. The write-truncates behaviour is a favourite one-marker: opening an existing data file in write mode and saving it erases the original data. Text files are read as strings, so numeric data must be cast after reading (int(line)) and cast to string before writing (str(total)) — forgetting the outbound cast is a common trace-error. CSV files split each line on a delimiter:
with open('scores.csv') as f:
for line in f:
parts = line.strip().split(',')
name = parts[0]
score = int(parts[1])Exam question pattern: 'A program must read a file that may not exist. Describe how the program should respond.' Full-credit answer: attempt the open inside a try block; catch the file-not-found exception; on failure print an error message and either terminate gracefully or fall back to an empty/default structure — the program must not crash with an unhandled exception or silently continue with no data.
Every Paper 1/2 includes a trace worth 4–9 marks. The method that protects your marks:
Worked trace — identify what this prints (AQA-style pseudo-Python):
nums = [4, 7, 2, 9]
total = 0
count = 0
for i in range(len(nums)):
if nums[i] > total:
total = total + nums[i]
count = count + 1
print(total, count)The trap: the comparison is against the running total, not a fixed value. Trace table:
| i | nums[i] | nums[i] > total? | total | count |
|---|---|---|---|---|
| 0 | 4 | 4 > 0 — true | 4 | 1 |
| 1 | 7 | 7 > 4 — true | 11 | 2 |
| 2 | 2 | 2 > 11 — false | 11 | 2 |
| 3 | 9 | 9 > 11 — false | 11 | 2 |
Output: 11 2. The algorithm keeps a running 'record so far' — it sums only values bigger than everything summed before them. If you traced against a fixed value such as nums[0] you would get 13 2 and lose the answer mark, though the table still earns method marks.
For 'state the output' questions on string manipulation, remember slicing is start-inclusive, end-exclusive ('revision'[0:3] is 'rev'), negative indices count from the end, and len, split, join, replace are the usual toolset. For 'write an algorithm' questions (OCR 2.2/2.3, AQA 4.1): declare a clearly-named subroutine, handle the edge case first (empty input, empty file), use a for-loop over indices when you need position and a for-each when you do not, and return rather than print inside a function — examiners penalise side-effecting functions. Finally, match the board's pseudocode conventions on the day: AQA exam pseudocode commonly indexes arrays from 1 in trace questions while Python code you write is 0-indexed; state your assumption if the paper does not specify.