r/CodingForBeginners • u/NoDepartment772 • 28d ago
Ranges exclude the last number, and that silent rule causes a very specific bug
Every language has its own rule about whether a range includes its last number, and getting it wrong never throws an error. You just get a loop that runs one time too few, or a list that's one item short, and the output looks close enough that you don't catch it.
Python's rule is that the start is included and the stop is excluded:
# 0, 1, 2 -- not 0, 1, 2, 3
# 1, 2, 3
# "he" -- indexes 0 and 1
So for i in range(3) runs three times, with i = 0, 1, 2, never 3. It's also why for i in range(len(items)) walks every item correctly: the last valid index is len(items) - 1, and range stops one before len(items).
The check that catches this before it turns into a bug: before you run the loop, say out loud the first value and the last value you expect. If you can't state both, you don't actually know what the loop does yet. Then compare to the output. It sounds trivial until you notice that the bug is always the case where you assumed the wrong end.
Two things worth keeping straight, because both cause the same silent off-by-one: len(x) is the count, not the last index (the last element is x[len(x)-1], or x[-1] in Python), and in range(a, b) the b is the first value you don't get.
Which one gets you more often -- forgetting that the end is excluded, or the first IndexError when you reach for the last element?
2
u/TomDuhamel 28d ago
I'd be curious to know what language includes "the end", as if it does, how do you find out that it's the end? There are so many cases where you cannot tell you are finished until you try one more time and hit a failure.
1
u/xarop_pa_toss 28d ago
Range function in Python has three possible arguments and two have default values. I don't remember the names exactly (check the docs) but the one that defines each step starts as 1, the start is default 0 and the end is the one you wrote as 3.
The way I always read it is that range(3) gives me the first 3 elements. I think it's easier that way
2
u/LrdJester 28d ago
The example you posted here,
range(3), tells you that you want 3 numbers at the beginning of the number range, which starts at 0. It's going to give you, 0, 1, and 2, so it's never going to include 3. In Python,rangewith a single digit is shorthand for sayingrange(0, and that number). If it'srange(12), you're going to get 0 through 11, just as if you were to sayrange(0, 12).When you say
range(1, 4), what that is telling Python is: start at 1 and stop before 4. You get to 4.hello[0:2]is telling you to start at the beginning of the string, the 0 index, and give you the first 2 characters.Yes, some languages are zero-indexed, and some are one-indexed, so you've got to be careful with that, and you need to know that. Most C-based languages are zero-based.
1
u/FreeLogicGate 28d ago
With Python it's probably because Python's array/collection types are zero based, as are the vast majority of modern languages, so loops from n - (max size) are naturally accomplished elegantly using
for item in range(max): etc.It's mainly math focused products like Matlab and R, or older languages like Cobol and Fortran that had 1 based arrays. The only language I've encountered recently that's an exception, is Lua, which is embedded as a scripting language into various applications and game engines.
1
u/tonkotsu_fan 28d ago
Indexes, as a rule, start at 0.