Why do arrays start at 0?
buttondown.email
buttondown.email
Whether or not the language presents the concept of "pointer" to the user is independent of whether or not it uses pointers internally. And if it exposes arrays as a concept, it has to implement them somehow.
The simplest possible implementation of arrays is having a start address and putting all elements next to each other in RAM. To get the address of a particular item, this layout naturally leads to the formula "base address + index * element size", with "index" being 0-based. If you want to expose other indexing schemes in your language, you'll have to add more logic to convert the user-visible index back to 0-based before you can obtain the address.
All of this is completely independent of the fact whether your language exposes pointers to the user or not.
Even the yacht story sort of hints at this:
> To keep people from having their jobs cut short by yacht-racing, Richards designed the language to compile as fast as possible. One optimization was setting arrays to start at 0.
If all indexing schemes were equal, why would this change even be an optimisation in the first place?
If 1-based indexing was used in assembly, then "mov 1(%ebx),%eax" would be the equivalent of "mov (%ebx),%eax".
at compile time, not runtime.
Not necessarily so for translation of an index that is evaluated at run time. PL1 (back in the 1960's) compiled arrays into 'dope vectors'. Each array's dope vector included highest and lowest valid subscripts for each dimension of the array and the address where the element with all zero subscripts would be found, if it existed.
The element at offset 0 is the first element.
Precision in naming is important.
It's also a definition commonly used in math, through not always. It depends a lot of the area of mathematics and even the cultural context.
The only reason we sometimes feel 0-index is wrong IMHO is because the english language describes entities in a sequence as 1st, 2nd, 3rd etc.
But I wouldn't be surprised if there is some human language somewhere which does it differently. Like something which roughly matches to english like "head, head+1, head+2, ...".
It really isn't. The human race gets by without it the vast majority of the time.
Which is sad, because all code is still executed as machine instructions even when the developer does not see it or does not want to care.
The code the programmer wrote is compiled to something such as LLVM IR, LLVM IR is further compiled by LLVM to Assembly, this is further compiled by an assembler into machine code, and then the c.p.u. further compiles this to it's internal code as it executes it. “machine code” really is no more special in this chain of events than, say, LLVM IR.
The easy way to do that is by shifting the base address. In pseudo-C (I think that computing the b pointer is non-conforming, even if the code never tries to access b[0], but compilers can do this without problems):
int a[100]; // array of 100 integers, zero-based
int * b = a - 1; // array of 100 integers, one-based
Things only get costly when you want to check array bounds or when you have multi-dimensional arrays. There also may be non-standard architectures where this kind of stuff isn’t possible.It feels like you're introducing a million edge cases.
[0] https://en.wikipedia.org/wiki/Performance_Handicap_Racing_Fl....
A pointer is just one kind of array-like indexing scheme. Good pointery languages will distinguish Address from Offset from Integer.
Now you'll just bring out the people who start the naturals at 1.
It all makes perfect sense in the context of measuring.
Well, no, obviously you want to index by ordinals, and they start at "first".
the positive integers (whole numbers) 1, 2, 3, etc., and sometimes zero as well.
Is that so? I wasn't aware of that.
It works better with the modulo operator: `array[i%length]` vs `array[(i+length-1)%length+1]`. Or you would have to define a modulo-like operator that maps ℕ to [1..n].
It works better if you have a multi-dimensional index, for example the pixels in an image. With 0 based indexing, pixel `(x,y)` is at `array[x+widthy]`. With 1 based indexing it is at `array[x+width(y-1)]`. You might argue that programming languages should support multi-dimensional arrays, but you still need operations like resizing, views, etc.
Consider:
Characters h e l l o
Cursor index 0 1 2 3 4 5
Char index 0 1 2 3 4
Range [0,3) [0,1,2]
Range [2,5) [2,3,4]
Range [1,1) []
If we used 1-based indexing and exclusive ranges, it leads to ranges where the end index is greater than the string's length... Characters h e l l o
Cursor index 0 1 2 3 4 5
Char index 1 2 3 4 5
Range [1,4) [1,2,3]
Range [3,6) (!) [3,4,5]
Range [2,2) []
but if we use inclusive ranges, it leads to ranges where the end index is less than the start index... Characters h e l l o
Cursor index 0 1 2 3 4 5
Char index 1 2 3 4 5
Range [1,3] [1,2,3]
Range [3,5] [3,4,5]
Range [2,1) (!) []
Also: Characters h e l l o
Cursor index 0 1 2 3 4 5
0-based range [0,3) [0,1,2]
1-based range [1,4) [1,2,3]
for the 0-based range [0, 3), the left array bracket is at cursor index 0, and the right bracket is at index 3. With 1-based indexing it doesn't work like that because the range is [1, 4)Iterating an array or adding to the end are fine, we have ipairs and insert for that, but ranges on strings I'm constantly having to think harder and write more code than necessary.
I love the language, wouldn't trade it for another, but the 1-based indexing on strings, which represents an empty string at position 3 as (3,2), it's egregious.
Not as egregious as a dynamic language where 0 is false though.
for (size_t i = 0; i < length; i++) ...
The following causes an unsigned integer underflow and is an infinite loop: for (size_t i = length - 1; i >= 0; i--) ... for (size_t i = length - 1; i < length; i--) ...
You are also free to start at any other (not necessarily in-bounds) index, just like with ascending iteration.[1] https://gustedt.wordpress.com/2013/07/15/a-praise-of-size_t-...
for (size_t i = n; i-- > 0 ;) ...
Or count from `length` to 1, but subtract 1 in the loop body, or count up and subtract the length in the loop body. Any modern compiler should be able to optimise these to be equivalent.In the majority of cases, counting down is not necessarily. Nor is ordered iteration. Most languages have a `for each` style syntax that's preferable anyway.
for (size_t i = length; i-- > 0; )...
It's surprising how often the issue pops, it works well with both signed and unsigned integers.(edit) I've started with one based indexing (basic)... mixed with 0 based (assembly), more 1 based (pascal), then more stuff (all zero based). I am, yet, to see a real advantage of a one based indexing... after the initial process.
for (size_t i = length; i > 0; i--) {
// ...
item = array[i - 1];
(This is how reverse iterators work in C++.) for (size_t i = 0; i < length; i++) {
size_t j = (length - 1) - i;
...
}
EDIT: change i to jNot hard to figure out.
To write 4 numbers (including zero) you only need two bits
0: 00
1: 01
2: 10
3: 11
To write 4 numbers if you avoid using the number zero, you need three bits 1: 001
2: 010
3: 011
4: 100
If you extrapolate that a little bit, you'll realize that you'll need two bytes (1 Byte + 1 bit from another byte) to store the indices of an array with 2^8 elements, which is just dumb.Some of you might be thinking: you don't need to store it the same way it's written, you can just substract 1 from whatever the user typed and convert it behind the scenes.
Yes, you could subtract 1, but then you would be making the whole system more complex, opaque, and unelegant, while hiding information from the programmer for no good reason.
0-indexed decimal grid:
0 1 2 3 4 5 6 7 8 9
10 11 12 13 14 15 16 17 18 19
20 21 22 23 24 25 26 27 28 29
30 31 32 33 34 35 36 37 38 39
40 41 42 43 44 45 46 47 48 49
50 51 52 53 54 55 56 57 58 59
60 61 62 63 64 65 66 67 68 69
70 71 72 73 74 75 76 77 78 79
80 81 82 83 84 85 86 87 88 89
90 91 92 93 94 95 96 97 98 99
1-indexed decimal grid: 1 2 3 4 5 6 7 8 9 10
11 12 13 14 15 16 17 18 19 20
21 22 23 24 25 26 27 28 29 30
31 32 33 34 35 36 37 38 39 40
41 42 43 44 45 46 47 48 49 50
51 52 53 54 55 56 57 58 59 60
61 62 63 64 65 66 67 68 69 70
71 72 73 74 75 76 77 78 79 80
81 82 83 84 85 86 87 88 89 90
91 92 93 94 95 96 97 98 99 100> It's not the reason you think. No, it's not that reason either.
Indexing is a language level concern, it has nothing to do with the generated code.
Unless you write machine code I guess.
IIRC, Visual Basic 6 took a why not both? approach to array indexing.
> These all point to one reason why 0-indexing might be preferred: it matches machine semantics more
https://www.cs.utexas.edu/users/EWD/transcriptions/EWD08xx/E...
“… you probably know that arrogance in computer science is measured in nano-Dijkstras.” — Alan Kay
I suppose the 0,1,infinity principle argues that including the empty set and smallest number is more important that including the largest number, so that notation should be preferred by default. And when writing using A<=i<B it is clear enough, but once you remove those symbols and start using them in indexing or function call syntax, like [A:B] or range(A,B), the inconsistency of bounds really breaks my brain, and having to pass a number larger than the largest array index as an upper index range bound gives me nervous ticks. I'd rather use inclusive bounds everywhere and have special syntax for empty set.
"Natural" zero-based counting at its best.
All room numbers were 5 digits, and skipped numbers if there were multiple windows in one room, based on the theory that such a room might later be sub-divided, and wanting to avoid renumbering of rooms down the hallway.
(I encountered this around 2003, so might have well changed since then; didn't find any references only with a quick search).
But then again people measure distance in feet and write dates as month day year :)
So there was a floor with rooms numbered 1xx and 0xx. There was also a floor below the 0xx floor. They just numbered it 00xx. And you could enter the building on any of those floors depending on what door you picked.
The class I taught was in 0005. Room 005 was someone's office. So on the first day of every semester we would have students waiting outside the door of 005 instead of 0005...
Floors 1 through 10 are "regular" floors. L is the lobby. P1 through P3 are the parking (basement) levels.
So you have three types: regular, lobby, and parking. And the elevator labels are of type regular | lobby | parking.
(Now you can't disagree with me because sum types are popular on HN, and I have framed this in terms of sum types.)
To add to the fun the only continuous corridor through the entire T-shaped building was on Level 10, one end of which (the base of the T) was at ground level on its part of the hill. There was a famous 'kink' in the middle of the top / cross-bar of the T (apparently the longest corridor in Europe) because they started building from each end and were slightly off.
This is a great example.
I wonder how many multiples of people who like arguing about starting from 1 vs 0 and how many people were confused for more than a day
The elevator, however, goes from floor '1' (the basement), to floor '5' (the top or fourth floor).
This really confuses visitors.
The Elevator Mafia want $10,000 to fix it.
So your initial assumption is incorrect from my experience.
I've seen setups like you mention, but they definitely aren't the majority in places I've lived.
The 0 makes a lot of sense in a C pointer world where memcpy and other alike functions can be written very thight.
The 1 makes a lot of sense in a human world, when we count, we start at 1, we talk about the "1st", counting on finger starts with 1, etc.
I once were at a Lua (1 indexed language) conference where this was discussed, and Luis started explaining why Lua was 1-index with this sentence: "The 1st argument ...." :)
Just think of 0-based as offset-based and 1-based as index-based. Both are intuitive just like that. I never get why people arguing over this bring pointers and memory (or anything computer related) to the table. No normal person is going to understand that, but everyone understands that if you don't move at all (0 offset) you stay at the first (index 1) item. Add one and ... you get the point.
Although often with an implicit zero. Under typical North American culture, your 1st birthday, for example, is more accurately the first anniversary of your birthday. Your birth is zero indexed.
TBH humans would have been better of if we were 0-based, it's just a convention. And we have the confusing language where "20th century" means 1900's. If we wanted to bring the 1-indexing we use in language to the fullest extent here to fix that particular issue, time counting would have to start at 1111. Except that won't work once reaching 5-digit years.
If we would start with "zeroeth" instead of "1st", then this would have solved itself and 20th century would mean 20xx's.
I especially don't understand why mathematicians use 1-based indexing (for matrix rows/columns etc...). Like programmers, they should see the advantages of starting at 0 (e.g. the coordinates of subdividing into block matrices are simpler if starting at 0). Mathematicians do start at 0 for the origin of plots, after all.
It is more like counting from 1 is just a leftover from times where zero was not commonly considered as number. Once one have zero, it makes sense to use it as an initial ordinal (see e.g. set theory, where zero is both initial ordinal and initial cardinal number, way before computers).
Another example is time and date, we start counting of days from 1, but counting of hours (at least in 24-hour notation) and minutes from 0.
> Luis started explaining why Lua was 1-index with this sentence: "The 1st argument ...."
Note that for spoken language, it is "The first argument ..." and 'first' is etymologically unrelated to 'one', but related to 'foremost', 'front', so it make sense to use 'first' for the initial item in the sequence even when using counting from 0.
99% of the times, the correct approach is to use iterators. When you really need indices (and you almost never do), 0 is more practical, because it matches the "including start, not including end" convention.
That is false; counting begins by initializing an accumulator to zero. When you register the first item, the count jumps from 0 to 1.
If I ask you to count the number of red balls in a bag with only 3 yellow balls, then the initial count in your head is 0, you inspect the balls one by one, never encountering a red ball, and thus never incrementing the count. And then you pronounce your final count of 0. So that's counting starting from 0.
What you call "starting at 1" is not so much the start as it is the first increment, which need not arise.
TBH humans would have been better of if we were 0-based, it's just a convention. And we have the confusing language where "20th century" means 1900's. If we wanted to bring the 1-indexing we use in language to the fullest extent here to fix that particular issue, time counting would have to start at 1111. Except that won't work once reaching 5-digit years.
If we would start with "zeroeth" instead of "1st", then this would have solved itself and 20th century would mean 20xx's.
Other than that - binary AND and power of 2 sized arrays are the backbone of any hashtable. Overall modulus (binary AND) is actually useful.
The problem it that most people, even programmers, don't understand how computer arithmetic works and have fantasies of mathematical number lines that the hardware only partially simulates. You see this consistently in the post-Java crowd who think that unsigned integers are some sort of unholy aberration because the languages they've grown up went further to maintain the fictional number line semantics.
Also, the time jump from 11AM to 12PM to 1PM! So I think more human friendly sometimes means more confusing.
Maybe distances should be human-focused, so that no displacement is equivalently expressed as 1 cm, 1 m, 1 km, ...
Then converting a distance d from m to cm is (d - 1) x 100 + 1.
Doing the reversal test, if programming languages had all been 1-indexed, then I doubt we'd hear much from people, in 2022, saying "I think the first element should be 0, and the second 1".
As a kid I learned "one one-thousand, two one-thousand, three one-thousand" when counting time out loud, but at some point I realized this was incorrect. The prefix is the start of the nth second but it isn't complete yet, so for example stopping in the middle of saying "two one-thousand" you actually haven't reached two seconds yet.
My fix was to move the prefix to the end, so I say "one-thousand one, one-thousand two, one-thousand three".
In retrospect maybe I should have used "zero one-thousand, one one-thousand, two one-thousand".
BTW on which level is the 1st floor?
Personally, I've always found zero-based more intuitive and satisfying, absent arbitrary indexing (in which case I'd chose zero-based for most purposes where there wasn't a compelling solution based reason to use a different index base). But then spent years writing SPL ... I
But I don't think the fact that many of us developed that intuition, makes it inherently better. It just makes it more intuitive for those of us who developed that inuition. I've written oodles of codes with both paradigms. Either works. Zero is just more natural, to me.
I have Martin Richards book on my shelf, and for a while shared an office with his coworker. I'm also one of the few folks here who has coded in BCPL.
Ok, so that said, this article is obvious nonsense. Zero indexing comes from the CPU itself. It's how you generate an indirect address by adding the offset. Many CPUs have this in hardware. They don't support non-zero offsets. The reason some languages have zero-based indexes is that their developers had the mind set of doing the same thing you do when writing machine code. They probably also wanted to directly interface between their language and libraries written in assembler (and the kernel was typically written in assembler too). Also they wanted to be efficient. Any offset other than zero is not efficient because you can't use the hardware addressing modes.
So then why do some languages _not_ have zero based indices? The obvious answer it that Mathematics by convention always used 1 as the base (or some arbitrary value). Many languages were conceived by mathematicians or people with a strong mathematical background.
Both have inconsistencies, like in Spain you might have a "middle ground" (entresuelo) which is neither 0 nor 1, but sits between, and is normally commercial or non-livable, reserving the 1 to the first floor where people live. I like that system better though because it usually goes 1 => 0 => -1 (underground), while in Japan you go 2 => 1 => -1, so I feel like it's missing a floor. You could justify it though as 0 being the ground line, so +1 is "the first above the floor and -1 is "the first below the floor", but I still prefer having each floor to be a natural consecutive number.
In older buildings, the ground floor is usually "1" - which means the floor above has to be "2".
type Day is (Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday);
type Hours is array (Day range <>) of Natural;
V : Hours (Monday .. Friday);
Which index type you should use depends on the problem that you're trying to model. You can find out the lower/higher end of a type/variable with the 'First and 'Last attributes.IIRC there's an RFC though to force the lower end of the index to a certain value like 1 or any other number/enum.
Turns out both were invented by the DoD.
>Due to the Department of Defense requiring as much of the syntax as possible to be based on Ada, in order to avoid re-inventing concepts that had already been thoroughly tested in the development of Ada,[citation needed] VHDL borrows heavily from the Ada programming language in both concept and syntax. - https://en.wikipedia.org/wiki/VHDL
Maybe I should pick up Ada soon. That could be a fun journey! (I really love writing VHDL)
The old way of doing it was setting the $[ variable to 1. You can actually set it to any value, in case you prefer to start your arrays at 42. It is now deprecated but you now have Array::Base that offers similar functionality.
Use it if you absolutely hate the person who will read your code, as if having someone read Perl code wasn't hateful enough.
> new Date()
Wed Aug 24 2022 ...
> new Date().getFullYear()
2022
> new Date().getMonth()
7
> new Date().getDate()
24
So 2022-08-24 comes out as (2022, 7, 24). One-based indexing for the year and day, zero-based indexing for the month."When dealing with a sequence of length N, the elements of which we wish to distinguish by subscript, the next vexing question is what subscript value to assign to its starting element. 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. And the moral of the story is that we had better regard —after all those centuries!— zero as a most natural number."
https://www.cs.utexas.edu/users/EWD/transcriptions/EWD08xx/E...
I'd be surprised if the use of zero vs one actually made any difference, from a number of operations point of view -- from the compiler's point of view, the first element in the array is the first element in the array, no matter what we call it.
I mean in an extreme edge case maybe if you are computing an index, and it happens to be zero, then in your math maybe some identity related to zero could be exploited (go to element simple_integer*complicated_function(), where simple_number might be zero) but that seems a bit silly.
In x86 you could probably hide it in the addressing but that does not mean it does not to be computed
1. Optimizing compilers exist.
2. Addressing at fixed offsets is cheap (a single instruction): https://en.wikipedia.org/wiki/Addressing_mode
0-based indexing is superior (as a default) because it simplifies a lot of common math, as discussed in TFA.
If that was true, Fortran, Julia, R and Matlab would use 0 instead of 1-based.
4. Macro expansion is often used to store indexes and other 'magic numbers' in Assembler, and this pattern originated either when Assembly was the primary programming language, or even earlier when humans directly punched out cards with machine instructions. Compilers in any remote sense of the luxury we have today did not exist or were not common.
char at(char matrix[10][10], char i, char j) {
return matrix[i][j];
}
still has more computation on 1-indexed than on 0-indexed, I believe.Same pitched notes are called "interval 1" and there goes thirds, fifths, sevenths... All off-by-one in my base-offset-addressing mind... And then major vs. minor which creates all kinds of "aliased addresses"...
I'd really love a BASE-12 floating number representation. Like 4.00 for the middle C; Chords can be then represented by a tuple of such numbers -- major = [+0.04, +0.07] (some sequencers already do something like that and I'm far better at reading that kind of sequencer data than a sheet)
It's possible to have a speculative argument and also refute other arguments that claim certainty. Pointing out another answer as being wrong does not mean one needs to know the correct answer or claim to have a precise answer.
⸻
1. As did all the other contemporaneous BASICs that I encountered. For added fun, when creating an array with DIM, you gave the highest index and not the number of elements so, e.g., DIM A(20) created a 21-element array. The 1980 Apple ][ manual I found which discusses both Integer BASIC and AppleSoft doesn’t admit that 0 is a valid index for an array, but elsewhere I saw it indicated as such which leads me to suspect that Integer BASIC has 1-based arrays.
This was true of even TI-99/4A BASIC, which was closer to Dartmouth BASIC and not written by Microsoft.
fruits = ['apple'] \\ Hello I have fruits
x = len(fruits) \\ Only one kind tho lol
fruits[x] \\ OK here is what I have
SUBSCRIPT OUT OF RANGE
I have always hated zero indexing for this reason (other than thinking in assembler where it is beautiful). It is of course useful in many contexts, albeit with tradeoffs; more importantly everyone is used to it and it's predictable. But I was absolutely thrilled to discover Julia defaults to indexing from 1 like a normal person.Essentially what we have here is a mismatch between the tool (computer that does everything in binary) and the task (mathematical abstraction of quantities). Now it's completely understandable that people work within the limitations of the tool when there is no other choice, but that's the same sort of path dependency that creates technical debt.
If zero indexing were so great, mathematicians would have made it the default centuries if not millennia ago; but mathematicians don't want to do off-by-1 adjustments on all sorts of common operations because it makes things unnecessarily complicated. To be honest, I think this has become a moat to keep people out of programming even though it's a shallow one.
It could be interesting to test this, say by taking two classes of schoolchildren and teaching one Julia and the other Python (or...). By not having to take on the idea of zero-indexing at the same time as the concept of an array, the Julia group can get into collections using their intuitive understanding of the natural numbers. I expect that picking up language elements quickly will have a compounding effect and that at the end of the evaluation period the Julia group will be able to make significantly more complex programs than a control group.
Edit: in your heart you know I'm right
They did. For any context where the index contributes to the math[1] (i.e. is not simply a label convention like "the x (first) component") the index always start at zero. Polynomials, Fourier series, geometric series, Bessel functions (both their order and their series expansions), etc.
[1]: Except, of course, when a divergent 1/0 term shows up.
Here is a nice discussion on stackoverflow https://cseducators.stackexchange.com/questions/5023/why-do-...
The first answer nicely retells the dijkstra argument: integer ranges should be described using half open intervals, and [n, m) is nicer than (n, m]. Furthermore, [0, n) is nicer than [1, n+1), and that's that.
The second answer makes the observation that there is a difference between indexing and counting, and that even in daily life often the first element is indexed by 0.
Still, I rather like indexing the first element of a sequence with 1.
Yeah, I think this exactly why it is zero based. The index became a simple multiplier for sizeof(int).
Don't get me wrong, it's why I like C.
If your language doesn't support zero-indexing, then you are stuck with 1-indexing, and that's that.
So, zero-indexing is the natural thing to put in a language, as it accommodates everybody.
In the past, I've written roughly half a million lines of Lua. Arrays don't always start at 0. I've regularly shaken my fist at Lua and cursed it's wicked ways, but it's really damn useful in a lot of contexts.
Arrays in programming use offsets.
for(int i=0;i<10;i++)
{
array[i] = x;
}
The error here is calling it 'i' for index. It should be 'o' for offset.Not all do [1].
1. https://therenegadecoder.com/code/which-programming-language...
The symbol changed over time as positional notation (for which zero was crucial), made its way to the Babylonian empire and from there to India, via the Greeks (in whose own culture zero made a late and only occasional appearance; the Romans had no trace of it at all)
Another meaning is "nothing" but we're talking about position, not nothingness
The mathematical zero and the philosophical notion of nothingness are related but are not the same. Nothingness plays a central role very early on in Indian thought (there called sunya), and we find speculation in virtually all cosmogonical myths about what must have preceded the world's creation. So in the Bible's book of Genesis (1:2): "And the earth was without form, and void."
https://www.scientificamerican.com/article/what-is-the-origi...
a[z * h * w + y * w + x]
But with 1-based indexing you need to substract one first from each component except the last: a[(z - 1) * h * w + (y - 1) * w + x]You know what would make all this confusion go away? Zero-based indexing.
Another example is if I'm getting directions from someone, and they say, "Walk 3 blocks that way", in my mind I conceptualize that I'm presently on the 0th block and I don't yet count it.
I don't think every day people disagree with these examples so much (well, maybe the birthday one, but that's because we use the same word for the literal day as well as anniversary), and they're all fairly intuitive. I never realized that 0-based indexing required such a deep background to be justified.
That depends on whether you think of "birthday" as synonymous with "the actual day of my birth" or "a celebration of the day of my birth". I think most of us actual consider it the latter, which is why the first one is after you've been born for a year. If you look up the definition of "birthday" (one word, no spaces) you'll see that there is often some acknowledgement of this in the differing definitions offered.
> "Walk 3 blocks that way", in my mind I conceptualize that I'm presently on the 0th block and I don't yet count it.
You have to decide whether the person is saying something is on the third block from your position, or you need to walk about three blocks of distance. You probably do this automatically based on how close you are to the edge of the current block. If I'm 30 feet from the edge of a black, I'm probably not going to include the current block in that distance. I imagine you probably won't as well.
> they're all fairly intuitive. I never realized that 0-based indexing required such a deep background to be justified.
I think they're only intuitive because we're all context sensitive. The issue is when people don't have the same context, or the context no longer strictly makes sense when the terminology is used in contexts that make less sense.
As an offset, and in programming languages where it's easy to see it's an offset (C), it makes perfect sense. In languages where that's all hidden from you, and you're really just referencing the nth item in a list, the 0th item doesn't make a lot of sense. People in these different contexts will likely have different ideas of what "intuitive" means with regards to this.
Something like the following IIRC: Type IntArray = Array [-10...10] of Integer;
0 based index makes sense to me. The memory location is offset 0 bytes from where the first data byte is stored (without getting into endianism).
I started programming in FORTRAN, then COBOL, then (Lord help me) RPG II. All had 1 based arrays. I got paid to program in RPG II eight hours a day, five days a week, for two and a half years.
A common bit of code was to create an array of month names. Then take an input date, say 20220825, and split that into $Year (first four characters) and $MonthNumber (next two) and $Day (next two).
$MonthIndex = strip the leading zero (if present) from $MonthNumber
Then, this code worked: $MonthName = @Month[$MonthIndex]
When I ran into my first language with 0 based arrays, and @Month[1] returned February, I was pretty much convinced you all had gone insane. December is an out of bounds condition now? Really?
Much later, a friend explained the stuff about multiplying the index by the array element length, and easily getting the memory location of the item in that array. It does make sense; but, I've always hated the code I've had to write that says $MonthName = @Month[$MonthNumber - 1]
You can now ask, "why does everything start with 0"? I guess because it gives you nice round (base-2) numbers. With 4 bits you can have 16 different "numbers", either 0 to 15 or 1 to 16. However, representing the number "16" in binary requires 5 bits, so you need an additional bit for the same number of elements. So representing 4 bits as 0 to 15 makes more sense
Constructing the naturals without including 0 and starting with it is incredibly awkward. Addition doesn't even have an identity.
Look at a clock, is 1 at the top, or is it the next one after the top.
Look at a ruler. Does it start at 1?
Do you want to have to shuffle everything around when you go from whole numbers to halves? Do you start at 0.5 or 0? Depends if it is "halves" or the real number 0.5
This is a recipe for pain and off by ones any time you're dealing with time steps (which is why it's so stupid that matlab and fortran are 1 indexed by default, if anything there's a better argument for C to be 1 indexed than scientific languages).
Now index and compose a bunch of ranges. A half open range of two elements is incredibly stupid 1 <= i < 3.
So closed ranges go with 1 indexing. Composing and splitting them is incredibly awkward [a,c] is split into [a,b], [b+1, c] ... that's kind of okay, but not unique as you could use [a,b-1], [b,c]. Now do [d,e], [e,f] compose at a glance?
Half open ranges go with zero indexing and they just work.
Either one is something you get used to with practice. I do prefer 0-based, but 1-based isn't as big of a deal as many people treat it in the vast majority of situations, and almost every situation where you'd be using something like Lua in the first place.
0 is a number, and just because the west ignored it for quite some time (read: "Zero: The Biography of a Dangerous Idea") we have these odd built-in aversions to it but in the end it makes plenty of sense.
0th element is 0, so why don't we just do a better job of educating kids to start counting with 0 and thinking about 0.
in the end - as a CS person, i think it's good - but i also don't mind if you disagree with me. :)
That would fix so many of these discussions.
...Of course, the answer is offset, because offsets are beautiful, composable, elegant things that harmonize with bitmasks, modulo, array slices, and everything else, while (one-based) indexes are nothing but "offset plus one", no more useful than "offset plus two".
In fact, it's so fundamentally more elegant that I've come to the conclusion that the real issue is that it's actually the mathematicians and language itself that got indexing wrong. Instead of "first" being associated with 1, it should be associated with 0. We should give athletes 0th place, and talk about the 0th man on the moon etc.
That's where the cognitive dissonance for the 1-based-indexing people is coming from. They can't deal with the fact that their "normal" way of indexing is wrong.
It's a bit like how pi should really be tau (2*pi), or the electron should really be positive. We got it wrong, but we're stuck with it because it's too much of a hassle to change it. Fortunately computing got it right! But now there's a mismatch with everyday life where people are used to the wrong thing.
- closer to hardware
- makes index == element wise offset from the first element
- there are both algorithm which are (very slightly) easier to implement with 0 and 1 indexing, AFIK more of the "bread and butter" algorithm are easier with 0 then 1 (e.g. indexing of C-style arrays used all the time, heaps etc.)
- 0 is not special => less bounds checks in typed languages (if you have an unsigned int index you only have to check the upper bounds, with 1-index you also have to check for 0)
- 0 is not special => with signed integer you can have negative indices (from the end) in which case all signed integers are valid (but potential out of bounds), with 1 indexing you have a "gap" in the middle which for some (rare) use cases can be very painful to handle
So independent of what the history was. It's just much more pragmatic to use 0-indexing in many common use-cases. Especially given that 1-indexing produces more special cases for very common use-cases makes it sub-optimal for both usability and performance.
>// if one prisoner fails all are dead (1st try -> tries = 0)
https://gist.github.com/amalic/c107b5289d946b758418778a95fdc...
That being said, it seems to be a junior software engineer issue where the brain isn't wired yet for "0-based" indexing. I don't hear such complaints from more experienced developers who have simply learned to appreciate it because like in my example above it is more intuitive to me.
I have added "and eventually future older me" above because I don't do much coding these days, and my career trajectory looks like it's going to be even less in the future.
However I did not understand why they would have a range in the early days of programming. On one hand it would have been hard to make a language and its compilers and then you add arbitrary ranges?
When you index starting at 1, you either have to add/subtract 1 internally, which sometimes but not always can be optimized away by a smart JIT/compiler, or you have to "waste" the first element in memory, trading memory for performance.
Lua always subtracts 1, while LuaJIT instead went with the latter approach.
print("Finger {}'s motor has overheated".format(i+1))However, it can also be useful for many purposes to allow arbitrary ranges, so a programming language probably should allow that; if the index of the first element is not zero, then the base address will not be the address of the first element (if the first element index is positive, then the base address may actually point into a different array, or a different variable). Then, if you have the base address and add the index, you will have the proper address of that element, whether or not the first index number is zero.
Zero is not always the most useful starting index; sometimes other numbers (which may be positive or negative) are useful. But, I think in general, zero is better.
Excerpt:
By a general rule, in B the expression
*(V+i)
adds V and i, and refers to the i-th location after V.
Both BCPL and B each add special notation to sweeten such
array accesses; in B an equivalent expression is V[i]
C was designed to be as close to the machine as possible. Prior to C the only programming language for systems programming was assembly. If the compiler-generated code subtracted 1 from the index every time you indexed an array that would have been seen as not as efficient as assembly.It would be easy for languages to offer two array access syntaxes, one for 0 and one for 1 like array[i] for 0 index and array{i} for 1 index access, then the accessing code could use the right tool for the job. Unlike defining the indexing type as a property of an array you shouldn't have problems with accidentally using the wrong one at time of use.
... but to really know if it's a good idea you'd have to test it, and sadly very few concepts in programming are subjected to any kind of rigorous scientific study.
If you think of arrays just as a kind of abstract list, then beginning at 1 makes much more sense. No one who looks at a to-do list at home talks about the 0th thing on their list to do. But the spacial nature of arrays makes starting at 1 confusing. After I take 1 step, I am no longer at my starting point. And cycling through arrays in code for me has a very similar feeling to taking actual physical steps through a data structure.
Like other languages, it is a collection of contiguous elements that can be selected by indexing.
Unlike other languages:
- any discrete type can be used for indexing, not just integers
- bounds can be any value (doesn’t have to start at 0 or 1. First index could be 13)
- one consequence of the aforementioned is that arrays are types that map to the problem domain, rather than being tied to the computing domain
- and more…
I'm happy to hear any dissenting opinions if this is inaccurate.
What? Is this some kind of trolling challenge? If you don't care about math, just say so. Most people don't. But making up a nonexistent convention is just silly.
If your arrays start at 1, you have indexes.
"0-indexed" shouldn't be a thing. I like the screenshot quote that refers to this as either "1-origin" or "0-origin".
Here's a discussion from some years ago that starts with some of Guido's thoughts on array indexing and moves on to the QuickDraw coordinate plane and points and rectangles, including how grid lines and points are infinitely thin/small, but pixels occupy the space between the grid lines.
In a minecraft clone I made, each chunk consists of 16 subchunks so that I only need 12 bits to represent the block position in the shader. Technically, the block position ranges from INT32_MIN -> INT32_MAX, but since each subchunk only consists of 16x16x16 blocks I can get away with storing the position in 12 bits and then passing in the subchunk's world position once as a uniform.
Lo and behold, it's written by the same author.
The origin of zero-based array indexing - https://news.ycombinator.com/item?id=6879478 - Dec 2013 (107 comments)
Zero-based arrays and the mythology of programming - https://news.ycombinator.com/item?id=6708409 - Nov 2013 (1 comment)
Citation Needed - https://news.ycombinator.com/item?id=6595521 - Oct 2013 (2 comments)
Threads about the EWD mentioned by jaapsen01:
Why numbering should start at zero (1982) - https://news.ycombinator.com/item?id=22162705 - Jan 2020 (220 comments)
Dijkstra's argument on why numbering should start at zero [pdf] - https://news.ycombinator.com/item?id=17850441 - Aug 2018 (1 comment)
Why numbering should start at zero (1982) - https://news.ycombinator.com/item?id=17765034 - Aug 2018 (63 comments)
Why numbering should start at zero (1982) - https://news.ycombinator.com/item?id=13186225 - Dec 2016 (216 comments)
Why numbering should start at zero (1982) - https://news.ycombinator.com/item?id=9761355 - June 2015 (47 comments)
Dijkstra: Why numbering should start at zero - https://news.ycombinator.com/item?id=777580 - Aug 2009 (71 comments)
Also these. Others? I'm a little surprised there aren't more.
Why do we count starting from zero? - https://news.ycombinator.com/item?id=17923391 - Sept 2018 (1 comment)
Why C Arrays Start at Zero: I Don't Know - https://news.ycombinator.com/item?id=11228267 - March 2016 (3 comments)
Why C Arrays Start at Zero: I Don't Know - https://news.ycombinator.com/item?id=11114704 - Feb 2016 (2 comments)
I'm sympathetic to its author's approach - we talk about indexing and it's a strangely fascinating topic but the history of it hasn't been all that well dug out. We do indeed tend to respond to articles like this by going "well obviously [x]-based is good because" - you can see that at work in this discussion here - but those are not necessarily the true historical reasons. But I think the author made a leap too far, the article was a bit too brittle, and it landed in a slightly too argumentative spot.
0-based or any arbitrary offset of your choice so that your data model can more closely map to what you need it to. And use eachindex instead of making assumptions and you can work with arrays using 1-based, 0-based, or arbitrary-based indexing.
VB is the only 1-indexed language I have used. VB is bad. Therefore 1-indexed languages must be bad.
I feel like I've also seen algorithms that work out better with 1 based indices, but not as many.
My manager thinks it was done to make the language easier to understand for people just getting into programming.
Lua's arrays are 1-indexed, and it is a terrific interpreted language! Array[0] is nil, makes sense. v5.1 has a solid JIT, and it's got a great 2D game engine called Löve2D.
Toddlers love this invariant.
If you don't understand this, you should not be writing computer software.
I've always considered the index and the power to which a base has been raised to be equivalent in many circumstances.
Thus starting at 0 makes sense.
The array indexer was syntactic sugar for pointer arithmetic.
1 is just as arbitrary as 2 or 79823 etc. Math wouldn't work without 0.
Thats good enough for me.
I've been annoyed recently by this obnoxious neckbeard behavior when they make things zero based for no reason (other than to conform to their supposedly existent philosophy), like Ratpoison and Screen selecting windows by a zero based index, which means you have to move your hand to the far right of the keyboard to select the first window with the 0 key, the far left to select the second window, with the 1 key, and one to the right to select the third window, with the 2 key.
I so happen to have been trying to work out a tangible explanation on paper of which system is better on trips a few weeks ago, and could not come up with one (and none are given in the article nor the top comments in this thread).
On a side note, the moment you say, "zeroeth", you can no longer be sure anyone knows what you're talking about because at any moment you may count in English or this pretentious UN*Xtard dialect of English. Seems like another pedagogical stumbling block held on to boomers and boomer-wannabes who just want to have their little counter counter counter culture or whatever.
You are confusing arrays with strings. Those used to start at 1, but since Unicode took over (~2007) strings are implemented as zero-based too (even though you can still think of them as 1-based when writing code).
Also in Delphi, when creating a cross-platform application, strings are treated as 0-based, with even multiple locations in documentation stressing this importance due to having enough differences between classic VCL and the new kid on the block (aka FireMonkey).
Especially the DBAD comment.
https://www.google.com/search?q=arrays+start+at+zero+meme&tb...
Sometimes you have a data structure called an "array", which means "a bunch of things that are the same size next to each other in memory". You can store the address of the whole array as the address of its first element, and offset into it by an offset; clearly, the offset of the first element is zero, but it's not the "zeroth" element.
And finally, sometimes you have cargo cult behavior by people who think that the way offsets into arrays work is because "numbering starts at zero in computers!" or some similarly wrong rationalization. This is when you get stupid things like (nth 0 list) in a lisp.
Because you predominantly think of indexing as the first definition as shown here[1], does not mean everyone else does. Be careful when using absolutes, especially when talking about languages.
A journey starts 0 metres from the origin.
If you count the number of _ you are along your journey and start with 1, then at the second metre you are at the 101st centimetre.
When someone says how many dragons have you slain? Just as you leave and haven't heard of any dragons yet, you don't say "I'm slaying my first dragon now"
Every construction of the natural numbers I have seen starts with 0.
The first hour of the day has passed when the clock strikes one. The hour preceding that is the same day. English calls it 12 because it was invented before we had a firm grasp of zero and english is inconsistent. 24 hour clocks call it 00.
1 based indexing can be correct, but 1 based ordered discrete sets map incredibly poorly to any ordered set with a different number of elements (such as the reals). Additionally dealing with ranges is less easy to make consistent.
0 based indexing is always correct.
However, when you examine the origin of the words, that is not the derivation. The derivation and path to English is quite complicated. You may feel a sense of righteous indignation or righteous repudiation on this topic. I would gently suggest deferring that feeling because I think the story is very interesting to follow.
That is not the end of the story though. The reason those pairs of words stuck around is almost certainly because it makes a consistent mental picture.
Bringing it back to the topic at hand - "Why we use 0" - because it works. There are many times when it has been evaluated, and at least to the people who design languages, it is more mentally appealing.
https://www.etymologynerd.com/blog/man-vs-woman
https://medium.com/interesting-histories/interesting-histori...