Tracing a Candidate's Marks-Statistics Program
In the practical examination a candidate must write a program that reads the marks of n students (each mark a whole number from 0 to 100), prints how many passed (pass mark 33), the pass percentage, and the average mark of only those students who passed. The candidate submits this logic:
INPUT n total = 0 : count = 0 FOR i = 1 TO n INPUT marks total = total + marks IF marks >= 33 THEN count = count + 1 NEXT i average = total / n PRINT count, count / n * 100, average
The examiner runs it with n = 4 and marks 25, 40, 60, 30, and it prints 2, 50 and 38.75. In another candidate's run with n = 0 the program stopped with a division-by-zero message. When the marks 90 were entered for a student, the program still ran without complaint. The candidate argues that the output is correct because 38.75 really is the average of the four marks entered.
1. Trace the program with n = 4 and marks 25, 40, 60, 30. Show the value of every variable at the end, state which outputs match the requirement and which do not, and answer the candidate's argument.
Read the worked answer
Trace.
| i | marks | total | count |
|---|---|---|---|
| 1 | 25 | 25 | 0 |
| 2 | 40 | 65 | 1 |
| 3 | 60 | 125 | 2 |
| 4 | 30 | 155 | 2 |
After the loop: total = 155, count = 2. Then average = 155 / 4 = 38.75, and the program prints 2, 2 / 4 * 100 = 50, 38.75.
What matches. The number passed (2) is correct - 40 and 60 are the only marks at or above 33. The pass percentage (50) is also correct.
What does not match. The requirement asks for the average of the students who passed, which is (40 + 60) / 2 = 50, not the average of all four. The program never accumulates the pass marks separately, so it has no way to compute the required figure. It also never finds the highest mark, if that is part of the specification.
The candidate's argument. The arithmetic 38.75 is arithmetically right but answers a question nobody asked. Correct execution of the wrong formula is still a wrong program; a candidate must read the requirement, not just check the arithmetic.
2. Rewrite the logic so it meets the requirement, including input validation and protection against division by zero. Explain each change you made.
Read the worked answer
INPUT n
IF n <= 0 THEN
PRINT "No students entered"
STOP
END IF
count = 0 : totalPass = 0 : highest = -1
FOR i = 1 TO n
INPUT marks
WHILE marks < 0 OR marks > 100
PRINT "Mark must be 0 to 100, re-enter"
INPUT marks
END WHILE
IF marks > highest THEN highest = marks
IF marks >= 33 THEN
count = count + 1
totalPass = totalPass + marks
END IF
NEXT i
PRINT "Passed:", count
PRINT "Pass %:", count / n * 100
PRINT "Highest:", highest
IF count > 0 THEN
PRINT "Average of passed:", totalPass / count
ELSE
PRINT "Average of passed: not defined (nobody passed)"
END IF
Changes and reasons.
- A separate
totalPassaccumulator, so the average is taken over pass marks only;totalis no longer needed at all. averageis computed inside a guardIF count > 0, because dividing by zero is undefined and crashes the run - the exact failure seen with n = 0.- An up-front check on n, since a zero or negative count makes the percentage meaningless too.
- Input validation, because a mark of 120 or −5 would silently corrupt both the count and the average.
highestinitialised to −1 so that any legal mark, including 0, replaces it.
3. In a later version the number of students is not known in advance: entry stops when the user types −1. State which loop you would use and why, and describe the two mistakes that most often appear when students convert a FOR loop into this form.
Read the worked answer
Use a WHILE (pre-test) loop with a priming read, e.g.
INPUT marks
WHILE marks <> -1
... process marks ...
INPUT marks
END WHILE
A FOR loop is the wrong tool because it needs the trip count before the first iteration, and here n is discovered only as data arrives. If the loop must run at least once and the prompt should appear before the test, a REPEAT … UNTIL loop with the read at the end is equally acceptable - the choice is about where the test sits, not about style.
Mistake 1: processing the sentinel. If the read is placed at the top of the body and the test at the bottom, or if the test is done after accumulating, −1 gets added to the total, increments the count and drags the average down. The sentinel must be tested before it reaches any arithmetic.
Mistake 2: forgetting the priming read or the second read. Omitting the read before the loop makes the condition test an undefined variable; omitting the read inside the body produces an infinite loop that never sees new input. Also keep the IF count > 0 guard - with a sentinel loop the count can legitimately end at zero.