Practice problem
Lowest Reading Of The Day
EasySolve in Python or JavaScript · graded against 2 sample tests and a hidden test set in your browser · Published
The Bug Report
The program reads one line of sensor readings and prints the smallest one. It runs, it never raises an error, and on most inputs the answer is right. On inputs that contain a 0 it is unreliable, and not in a way that always looks the same.
Input that shows the problem
4 0 9 2What the program prints
2What it should print
0The 0 in second place is plainly the smallest reading, and the program reports 2 — the value that happens
to be last in the list. Swapping the input around to 5 0 gives the right answer, while 0 5 gives 5.
Whether the answer is correct seems to depend on where the 0 sits, which is the sort of detail worth
following.
Input Format
The input is exactly one line: the readings, as integers from 0 to 1,000,000 separated by single spaces. There is at least one reading.
Output Format
Print one line containing a single integer: the smallest reading in the list.
How to Debug It
The report already hands you a pattern — the answer depends on where the 0 is — so start by making that
pattern as small and as sharp as possible. Two readings is enough. Run 0 5 and then 5 0 and write both
results down. One of them is right and one is wrong, from the same two values. A bug whose behaviour flips
with the order of the input is almost always in the loop's decision, not in how the input is read.
Next, make the state visible instead of imagining it. Print smallest at the top of each pass, and print
which branch the condition took, then run the failing four-reading input. Reading down that trace, look
for the moment the value stops being the smallest seen so far and starts tracking whatever the current
reading is. You are looking for the first line of the trace where the loop overwrites a good answer.
That moment tells you what value smallest was holding when the condition misbehaved, and that is your
hypothesis: one specific value of smallest makes the condition say yes when it should say no. State it
out loud, then check it by asking what the first half of the condition actually evaluates to for that
value — in a shell, in the playground, anywhere. Change one thing: how the condition asks "have I seen a
reading yet?" Re-run both two-reading inputs, the failing sample, and then the second sample, which was
passing before and must still pass after.
What Was Wrong
The condition was if not smallest or reading < smallest: in Python, and if (!smallest || reading < smallest)
in JavaScript. The intent is clear and the shape is common: smallest starts as a sentinel (None /
null) meaning "nothing recorded yet", and that first test is supposed to catch the very first reading,
before any comparison is possible.
The trouble is that it does not test for the sentinel. It tests whether smallest is falsy, and the
sentinel is not the only falsy value a reading can produce. 0 is falsy too — in both languages. So as
soon as a 0 becomes the smallest reading, every later reading sees not smallest (or !smallest) as true
and overwrites the answer, no comparison needed. The loop stops tracking a minimum and starts tracking
whatever it saw last. That is why 4 0 9 2 reports 2: the 0 was found correctly, then discarded by the
next reading, then by the next.
The fix is to ask the question the code meant to ask, which is about the sentinel specifically:
if smallest is None or reading < smallest:if (smallest === null || reading < smallest) {is None and === null are true for the sentinel and for nothing else, so a legitimate reading of 0
stays put once it wins. Note that JavaScript's loose != would reintroduce a smaller version of the same
problem, since null == undefined is true; strict === against the exact sentinel you chose is the
version that keeps meaning what you wrote.
Common Mistakes
- Starting the accumulator at 0 instead of a sentinel. It removes the falsy-zero trap but replaces it with a worse one: 0 is now the answer to beat, so any list of positive readings reports 0, a value that never appeared in the input. A sentinel that cannot collide with real data, or seeding from the first element, is what makes the first comparison honest.
- Keeping the truthiness test and special-casing 0 alongside it. Two conditions that have to agree about what "nothing yet" means is one more thing to keep in step, and it still fails the day the readings are allowed to be negative or empty strings. One explicit sentinel test covers every case at once.
- Assuming this only matters for 0. Falsy is a longer list than most people hold in their heads:
in Python that includes
0,0.0,"",[]and{}; in JavaScript0,-0,"",NaN,nullandundefined. Any of them can be a perfectly valid value in some problem, and a truthiness test quietly treats them all as "missing".
Sample tests
Sample 1
Input
4 0 9 2
Expected output
0
The smallest reading is the 0 in second place. The broken program walks straight past it and reports 2 instead.
Sample 2
Input
7 3 11
Expected output
3
No reading is 0 here, so the broken program happens to get this one right — which is exactly why the bug survived.
Your solution
Run tries the first sample. Submit grades against every sample and the hidden tests. Your code is saved in this browser as you go. This problem accepts more than one language: pick yours above the editor, and each keeps its own work while this page is open.
Press Esc then Tab to move keyboard focus out of the code editor.
Output will appear here after you run your code.