Card 03/ 08
Tracing the Finished Code on the Hand-Worked Examples
Run the listing by hand on the examples from step two, edge cases included, before trusting it with anything larger.
Tracing is the sixth step: following each variable through the code, line by line, on an input small enough to hold in your head, and checking what the code produces against the answer worked by hand. This card traces a function that finds the first ticket scanned twice at a gate, on the scans 7 3 9 3, then runs it in Python, Java and TypeScript on that input and four edge cases: no scans, one scan, every scan the same, and no repeat. The hand-worked examples become the tests, which is why they are written down before any code.
The trace follows the set of tickets seen after each scan. An instrumented copy of the Python function printed it, showing the set in sorted order.
scan 1: 7 is not in the set (empty), add it
scan 2: 3 is not in the set 7, add it
scan 3: 9 is not in the set 3 7, add it
scan 4: 3 is in the set 3 7 9, return 3Each edge case guards one line. No scans checks that the loop can run zero times and still return. One scan checks that a ticket is never compared with itself. Every scan the same checks that the repeat is caught at the second scan rather than the last. No repeat checks the return after the loop. Each listing prints one line per input; the highlighted lines are the membership test and the insertion, whose order is what keeps a ticket from matching itself.
The first repeated ticket on five inputs, in each language
def first_repeat(scans: list[int]) -> int | None:
seen: set[int] = set()
for ticket in scans:
if ticket in seen:
return ticket
seen.add(ticket)
return None
examples = {
"ordinary": [7, 3, 9, 3],
"no scans": [],
"one scan": [5],
"all the same": [5, 5, 5],
"no repeat": [7, 3, 9],
}
for name, scans in examples.items():
result = first_repeat(scans)
print(f"{name}: {'none' if result is None else result}")ordinary: 3
no scans: none
one scan: none
all the same: 5
no repeat: noneimport java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
public class FirstRepeat {
static Integer firstRepeat(int[] scans) {
Set<Integer> seen = new HashSet<>();
for (int ticket : scans) {
if (seen.contains(ticket)) {
return ticket;
}
seen.add(ticket);
}
return null;
}
public static void main(String[] args) {
Map<String, int[]> examples = new LinkedHashMap<>();
examples.put("ordinary", new int[] {7, 3, 9, 3});
examples.put("no scans", new int[] {});
examples.put("one scan", new int[] {5});
examples.put("all the same", new int[] {5, 5, 5});
examples.put("no repeat", new int[] {7, 3, 9});
for (Map.Entry<String, int[]> example : examples.entrySet()) {
Integer result = firstRepeat(example.getValue());
System.out.println(example.getKey() + ": " + (result == null ? "none" : result));
}
}
}ordinary: 3
no scans: none
one scan: none
all the same: 5
no repeat: nonefunction firstRepeat(scans: number[]): number | null {
const seen = new Set<number>();
for (const ticket of scans) {
if (seen.has(ticket)) {
return ticket;
}
seen.add(ticket);
}
return null;
}
const examples: [string, number[]][] = [
["ordinary", [7, 3, 9, 3]],
["no scans", []],
["one scan", [5]],
["all the same", [5, 5, 5]],
["no repeat", [7, 3, 9]],
];
for (const [name, scans] of examples) {
const result = firstRepeat(scans);
console.log(`${name}: ${result === null ? "none" : result}`);
}ordinary: 3
no scans: none
one scan: none
all the same: 5
no repeat: noneAll three print the same five lines, and each matches the answer worked by hand. Java returns Integer rather than int so that null can mean no repeat, and TypeScript returns number | null for the same reason. A return of −1 would also work here, but only because the clarify step established that tickets are positive; on input that may hold −1, the sentinel would be indistinguishable from a real answer.
← → move through sections and cards