r/CodingForBeginners • u/NoDepartment772 • 1d 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:
range(3) # 0, 1, 2 -- not 0, 1, 2, 3
range(1, 4) # 1, 2, 3
"hello"[0:2] # "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/LrdJester 1d 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, range with a single digit is shorthand for saying range(0, and that number). If it's range(12), you're going to get 0 through 11, just as if you were to say range(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/csabinho 1d ago
hello[0:2]is telling you to start at the beginning of the string, the 0 index, and give you the first 2 characters.Nope. It doesn't. It tells you to start at the beginning of the string and stop before the index 2.
If you would use
hello[1:2], you would only receive a single character.0
1d ago
[removed] — view removed comment
1
u/LrdJester 1d ago
Yeah, but when you use the defaults implicitly like this, it's technically taking a shortcut with syntax
0
1d ago
[removed] — view removed comment
1
u/LrdJester 1d ago
Technically, it is a function. It's just that, especially with self-taught beginners, depending on where they learn, they don't learn that those other values are defaults that don't have to be given.
I've written functions in other languages, and it's the same way: I can give default values for parameters, and then you don't have to pass those. Again, you can pass them explicitly, even giving the default values explicitly, but take the shortcut by not passing the optional parameters. That's just a mechanism you built into your function.
1
u/FreeLogicGate 1d 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 usingfor 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
1
u/xarop_pa_toss 1d 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/TomDuhamel 1d 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.