He ends with the assertion "In corporate religions as in others, the heretic must be cast out not because of the probability that he is wrong but because of the possibility that he is right." This sounds profound and edgy but is just an appeal to contrarians which can be used to justify absolutely anything. You criticize my theory X, that just shows how right I am! And anyway, isn't the "corporate religion" these days to start with 0, since this as what the big mainstream languages does?
Exactly this. Our firm had been forced to change numbering of real-life items from 1-based to 0-based, because some cargo-cultist somewhere was amazed by the offsets and addresses and wanted to force this numbering scheme everywhere. Now there is a code inside that converts 1-based items to 0-based for legacy stuff, and we are still finding bugs caused by that. Every day to day operations are artificially harder because understanding what is actually unit number 1 or port number 2 is hard now, just like counting total number of anything and remembering to add 1 afterwards. But now we get to have a hype 0-based everything system, which nobody asked for. In some cases this can lead to such awesome combos like physical ports numbered 1-based, then 3rd party switch with 0-based, then Linux interface on top 1-based, then our config on top 0-based. Amazing and convenient, isn't it?:)
And if I want to count the items in my array I start at '0' and keep going until I get to '12'. Even though there are 13 items in my array?
I see.
zero-indexing is good for system-level stuff, for dealing with actual memory and so on, but I'm still not convinced it's the right way. It's certainly not the one true way, 1-based indexing is completely valid for some use-cases like maths.
Either way, there will be off-by-one difficulties, either in ordinals or count or whatever, so it's a trade-off.
for (int i=0; i<13; i++) {
whatever(array[i]);
}
and
def printHello():
print("hello world"[0:5])
for decades. No point vastly messing up consistency for minimal gain. BTW notice how in both cases we count to n elements despite using 0..n-1 indexing. You don't, in fact - need to put 12 anywhere if you mean 13 elements. whatever[1]
gives the second item, and the last item, of 13, is whatever[12]
fortran has been 1-based since well before these examples.The point is there is no one true way, it's a trade-off.
If you have a two dimensional array A (1..N X 1..M), then flattening it becomes A[i][j] = flat[i + (j-1) * N]
Continuing to higher dimensions it only gets uglier.
0-based arrays are far simpler. When flattening A (0..N-1 X 0..M-1) A[i][j] becomes flat[i + N * j].
It's completely analogous to how our base-10 positional system works. Each digit doesn't start at 1, they start at 0, which is why 237 is just 2*10^2 + 3*10^1 + 7*10^0.
ADD: TIL HN supports escaping the astrix.
> When flattening A (0..N-1 X 0..M-1) A[i][j] becomes flat[i + N * j].
To play the devil’s advocate a bit: Both cases above has the same number of “-1” offsets; the question is whether it is in the indexing or in the bounds. In the 0-indexed case, i goes from 0 to N-1 instead of from 1 to N, and this happens for every array dimension.
Another common example of this is circular buffers. Computing the bounded index from an arbitrary index is simply i % c with zero based indexing, but becomes (i-1) % c + 1 with one based indexing.
Arguably, this is because the common definition of modular arithmetic is itself zero-based: “modulo N” maps all integers into the numbers [0,N-1]. It is fully possible to define a “%” operator that instead mapped the integers into the range [1,N], which might be more natural in 1-based languages?
If directly accessing byte-addressable memory, the command, 'go to address 0xff and get me the data stored in the ten bytes beginning at 0xff' will return data at 0xff + 0, 0xff + 1, ..., 0xff + 9. Hence counting should start at zero, with the zeroth byte. Describing this sequence as range(0, 10) is convenient since 10 - 0 gives us the count of bytes in the sequence.
If accessing objects in an linear data structure via an interface, at a higher level of abstraction, then a natural choice is to label the objects starting with one, i.e. get the 1st object's data, get the 2nd object's data, ..., get the 10th objects's data. Note, range(1, 11) still works as 11 - 1 gives us the total object count. (Python seems to get this right in both cases, i.e (inclusive, exclusive)).
However, if one is also using low-level byte-addressable memory at the same time, this can get unnecessarily confusing, so it's probably simpler to just always count starting at zero, for consistency's sake - unless you're writing a user interface for people who've been trained to start with one?
Think of a basket of apples. If you take the zeroth apple (empty basket), you have no apples. If you take the first apple, you have 1 apple and now the basket is empty again (0).
One also wouldn't have to do [length - 1] to access the last index; it would naturally be [length].
table = {apple0, apple1, apple2, apple3}; //length == 4
hand = {}; //length == 0
after table = {apple0, apple1, apple2}; length == 3
hand = {apple3}; //length == 1
You count apples in hand by looking at how many items there are, not by what indexes they have.For example, given a array with size N in Python, we can iterate it from 0 to N-1 (onwards) and from -1 to -N (backwards), which is not consistent at all.
Programming language is meant for human eyes. It would be better if array index being 1 to N, and let the compiler substract that 1 for us.
The consistency is that if i < 0 and l[i] exists then l[i] = l[i+N]
> Adhering to convention a) yields, when starting with subscript 1, the subscript range 1 ≤ i < N+1; starting with 0, however, gives the nicer range 0 ≤ i < N. So let us let our ordinals start at zero: an element's ordinal (subscript) equals the number of elements preceding it in the sequence.
"A nicer range." This is just Dijkstra's preference.
Interestingly, he links ordinals (the subscript) to cardinals (how many numbers there are before the element).
The off by one is just as likely in both schemes with half open intervals.
The nice property of the length of the sequence falling "for free" from the indices is not essential if you track the length separately. While it was important in the age of machines with severely constrained memories, the downsides outweigh the merits.
C uses pointer offsets. Base pointer + offset = destination address. C "array" syntax is sugar for this addition. There are no array indices in C, just pointer offsets. Thus, they start at 0.
Some languages have arrays that aren't just a pointer to the first element. In those, indices are a better option. There, starting at 1 usually makes more sense.
The first number used should match the use of that number.
In most of Europe ground floor is 0, underground floors are negative, floors above the ground are positive numbers.
Integers were invented for a reason, use them :)
Counting things (measuring how many you have) starts at 1 out of convenience. It makes no sense to talk about all the 0 things (that you don't have ) to start. Communication tends to be as succinct as possible.
In software, we often are tracking multiple things, so being explicit about how many offset or what an int holds, causes 0 to appear more frequently and "feels better" because the explicit communication is comprehensive.