Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

I don't get why zero-indexing requires so much justification. Here are two powerful reasons why it makes sense:

    * Zero is the first unsigned integer. If you start at 1, you're wasting one
    * p[0] == *p, and it makes no difference whether pointer arithmetic had been invented, because pointers are just memory addresses, and that's what the cpu works with
The best reason for one-indexing:

* Some people think it's less confusing

>> Over and over again tools meant to make it easier for humans to approach big problems are discarded in favor of tools that are easier to teach to computers, and that decision is described as an inevitability.

That's exactly how it should be. What's easier for humans is subjective. What's easier for computers is objective, and going with the grain of the machine tends to yield solutions that work, whereas going against the grain tends to cause over-complicated and buggy implementations. That's the true take-home of "Worse is Better." The Unix approach to syscalls worked, and Unix is alive and well. The MIT approach was quite possibly impossible to implement, and the system they were building is extinct. If you want to use a computer well, you have to learn a little about computers and set aside your preconceived ideas. What's so unreasonable about that?



This is just all very subjective:

  * Zero is the first unsigned integer. If you start at 1, you're wasting one
Counter-argument: If you start indexing at 1, it allows you to encode an exceptional condition (e.g., element of a search not found) as 0 when the result is an unsigned integer.

  * p[0] == *p, and it makes no difference whether pointer arithmetic had been invented, because pointers are just memory addresses, and that's what the cpu works with
Counter-argument: This only holds for fixed-length arrays, strings, and the C/C++ idiosyncrasy that conflates pointers with arrays (at the expense of runtime safety). If you have variable length arrays or strings, then the beginning of the actual array/string contents will be at an offset (allowing for length/capacity/etc. fields). In fact, the offset may be exactly one (if you have a length field the size of each element), making one-based indexing the more efficient choice.

You can make up ANY number of arguments for zero- or one-based indexing, and it will depend entirely on how much weight you assign to each of them.

Or, alternatively, you can use the Pascal approach, where you can specify the beginning index on a per-array basis.


Using 0 as a special error value is a kind of a hack though. What you're really doing is returning "either an error or an index", which is a different type than "an index". Empirically, using one language type for two concepts like this, allowing errors to be silently dropped as values can be implicitly converted from one to the other, has been very error prone. See the discussions about nullable pointers for example.

WRT strings with metadata, if you construct your data structure so that the length and other metadata are at negative offsets from the pointer value that gets passed around, then the array data can be at offset zero again, so it's even more efficient. So zero still wins :).

But the arguments for 0-based indexing are also deeper than C arrays. C arrays are just one place where it's clearly visible.

The Pascal approach unfortunately doesn't solve the problem either. "Everyone can do what they like" is fine as long as no one ever has to read or interface with code written by other people.


> The Unix approach to syscalls worked, and Unix is alive and well. The MIT approach was quite possibly impossible to implement, and the system they were building is extinct.

The "MIT approach" in Worse is Better discussed the extant PCLSRing scheme on ITS (not a theoretical one) and the different approach that Unix took. Saying that it was "impossible to implement" is just flat out wrong.

ITS is an ancient time sharing system that predates Unix. It was written in assembly, ran on PDP-6s and later on PDP-10s, and had fallen into disuse when Gabriel wrote that essay (MIT would stop using it the following year). The success of Unix had nothing to do with its solution to the PCLSRing problem.


I see. It was already in production. It's an open question whether the more complex syscall conventions would have held up over time as new features were added. It makes a lot of sense in general to push unnecessary complexity out of the kernel and into userspace, since processes in userspace are allowed to fail. The complexity of the system call interface can be hidden behind a library.


It's interesting to consider whether PCLRing might have worked. I'll look at it in more depth.


"Some people" is understated; if you're holding one orange and I ask for your first orange then you give me that one, in your first hand. I'm over-emphasising but the point is that no zeroes are involved.

It's a trade-off, if we were bending entirely to the computer's will we'd all be programming machine code, whereas there's overhead in using more natural language. Honestly I've bought this 0-indexing is the Right Way for a long time but the more I use R the more a 1-index seems to make sense for high-level languages.


An index is the beginning of a unit - the point at which it is found, where it begins. The index doesn't represent the unit itself, just where it is located.

Your orange takes the zeroth index becoming the first orange: http://i.imm.io/1lWRO.png

People understand the concept of zero perfectly. They know the day doesn't begin at 1am, they know babies aren't born 1 years old. This isn't a computer-specific concept in any way.


> They know the day doesn't begin at 1am, they know babies aren't born 1 years old. This isn't a computer-specific concept in any way.

This is true, and an interesting point, but also nobody describes a baby as being zero years old, or the time as being zero-o'clock. People avoid zero in these situations and find alternatives - i.e. 3-months or 12.30am.

Personally I see both sides but the point is that whenever you make something divisible, the first unit is 1 and that's what people call it.


Actually, in the US military, it's common to refer to the first hour of the day as zero. As in, "zero hundred thirty hours" for 30 minutes after midnight.


That the clock starts at 12 has annoyed me for quite a long time (i.e., 12PM comes before 1PM). I kind of wish that non-military usage also used 0:00-11:59, if only for the logical consistency of it.

Alternatively we could just switch 12PM and 12AM, but that would mess with midnight == start of the day.

Unfortunately I think I'm doomed to suffer through this inconsistency, since it doesn't seem to bother anyone else.


Why not use a 24 hour clock then? In Europe, 24 hour (digital) clocks going from 00:00 to 23:59 are commonly used. My French friends even use it even when speaking (I admit is kind of weird to hear "see you at sixteen thirty"), while in other countries such as Italy they use 24 hour clocks as well, but they "translate" it when speaking.


Yeah, I think that would be better.

I guess for me personally the perfect solution would be a 12-hour clock that operates on the same principles as the European 24-hour (which I think is the same as the military clock?). So 00:00 to 11:59, twice per day.

But honestly my whole argument is pretty academic, so I'll probably just put up with the clock starting at 12.


This confuses measuring continuous variables with discrete indexing. I'm certainly not arguing that zero isn't a well-understood concept.


> I ask for your first orange then you give me that one, in your first hand

It's the difference between ordinals and cardinals. In human languages I see ordinals as a pure shorthand: you could just as well say "give me orange number zero", except it's more cumbersome. I honestly can't think of a context (in human languages) where ordinals would be something more than a mere convenience. I believe the existence of ordinals is a technically unnecessary historical artifact: counting was invented before zero.

PS: I doubt people number their hands. A person would say "in your left hand", not in "your first hand".


> if you're holding one orange and I ask for your first orange then you give me that one, in your first hand. I'm over-emphasising but the point is that no zeroes are involved.

No ones are involved either. I don't have a "first" hand and when you request a first orange, you're just asking for the head of the ordered set.


I get it, believe me I do, but pedantry aside, we know how any non-computer scientist or programmer would address the initial item of any sequence (indeed the same way a mathematician would).


Well, frankly, I always thought 0-indexing made perfect sense, given the elegant symmetry with pointer arithmetic in C. It also makes sense in that variables are naturally initialized to zero, and that puts an index right at the start of an array. Everything just falls into place.


Same thing with Erlang... it seemed so weird at start... now I hate all 0-indexed languages.


Well, you don't have to use the 0th index, it's better than wanting to in a language where you can't!


> I don't get why zero-indexing requires so much justification. Here are two powerful reasons [...]

Reasons which I feel are too C-like and high level already. The article falls into the same trap and says this as a counter argument:

> It’s not about * i = a + n * sizeof(x) because pointers and structs didn’t exist.

But I actually read this as an argument in favor of zero-based index origin: since pointers and structs did not exist, any form of memory addressing and data storing/retrieving had to be done at some lower level, and if you did it by hand, you will for sure align the start of your in memory handmade array-like structure at the address of it, then merely add to the register holding the address an offset maybe itself stored in a register, and certainly you would not add 1 again to those, because the idea would not even cross your mind to lose one and one byte/word of memory and one cycle, as it would just make no sense at that level.

Subsequently, C (or B or A or whatever) evolves the process and embeds into the language such arithmetics, abstracting it under monikers such as "pointer", "struct" and "array" and adding utility functions like "sizeof", so that you don't have to remember if this or that CPU/asm uses ADD or ADL, or is foo-aligned, or uses bar calling convention.


In my opinion, one-indexing is better:

* Ordinal and cardinal numbers both start from 1 in all major human languages

* Humans didn't use the number zero until relatively recently in mathematical history - this illustrates that it's intuitively hard for humans to grasp.

* It's important to separate the cases of 0 and null, which is easier if you use 1-indexing. This has got to save a lot of tricky bugs.

High-level programming languages are designed for humans. Humans 1-index everything, so these would be better 1-indexed.

Low-level programming languages are designed for computers. You can 0-index there, for the reasons that you stated.

As programmers, we had to explicitly learn 0-indexing. 0-indexing creates an artificial barrier to enter programming, when there are so many other barriers that we should be trying to relieve.


It's true that humans hadn't discovered zero until relatively recently in mathematical history. Should we reject these "new" developments in numeracy, and go back to roman numerals? One could make a pretty strong case that roman numerals are more intuitive, more human.


I take your point here - but no developments in numeracy include counting objects from zero.


That's because making the switch from 1-indexing to 0-indexing is extremely counter-intuitive. However, take a hypothetical person who learned 0-indexing socially from birth, I'd imagine they'd be just as confused by 1-indexing.


When you use a count variable in programming, do you initialize it to 1?


Making this argument agrees with my general point: that high-level programming languages ought to count as humans do.


My point was that it's rather awkward to count from 1 when doing programming.

For instance, could you rewrite this function where int count = 1; and have it still be intuitive?

  int count_occurrences(std::vector<int> vec, int value) {
    int count = 0;
    for(int i = 0; i != vec.size(); ++i) { // using 0-based index
      if(vec[i] == value)
        ++count;
    }
    return count;
  }
My attempt is: (look how ugly!)

  int count_occurrences(std::vector<int> vec, int value) {
    if(vec.empty())
      return 0;
    count = 1;
    for(int i = 2; i <= vec.size(); ++i) { // using 1-based index
      if(vec[i] == value)
        ++count;
    }
    return count;
  }


As another commenter pointed out, it's only the indices that are one-based. I think it would look something like this:

  int count_occurrences(std::vector<int> vec, int value) {
    int count = 0;
    for(int i = 1; i <= vec.size(); ++i) { // using 1-based index
      if(vec[i] == value)
        ++count;
    }
    return count;
  }
Off the top of my head, this has a number of benefits in higher level languages. For instance in Javascript, here are some common inconveniences caused by zero-based indices:

  var lastEl = arr[arr.length - 1];

  if(arr.indexOf(someEl) !== -1) {
    // do something knowing that someEl is in the array
  }
and if we lived in a one-based world, here's what they would look like:

  var lastEl = arr[arr.length];

  if(arr.indexOf(someEl)) {
    // do something knowing that someEl is in the array
  }


It's only `i` (the index) that's meant to begin from 1.

`count` is a quantity, not an index, and there are such things as "zero apples". But you start counting them from the first one.


I agree that it's not at the same level, but there certainly are developments.


Computers aren't a development in numeracy?


point 1 is tautological. point 2 is incorrect. point 3 is a problem of the concept of null being a mistake, not of 0-indexed arrays.

programming is math, and all the math works better if 0-index arrays. Leave programming language design to the adults.


1-indexing is no longer intuitive when numbers become large. Years are 1-indexed; how many people observed the start of the millenium in 2001?


You don't index pointers by pointers, so I don't see point 3. Your types distinguish zero from null.


>> What's easier for humans is subjective. What's easier for computers is objective.

Just want to point out that perhaps we humans are not as subjective as our intuition indicates. The third rear brake light make car's stopping distance shorter. There are human (psychological) reasons for that. I mean you could do objective approach all you want to make traffic safer, like build better brakes, but ultimately, humans control cars, humans have biased (arguably fixed) way of thinking, and there are ways to create interfaces that makes use of that bias. 1-based indexing makes ton of sense when your tool is meant for human use, because most of us is biased towards start counting at 1, objectively at least.


What I meant to say is that people are arbitrary and capricious and, in many cases, will never agree on what is easier. The objectivity of the practical engineering concerns offers a rational basis for making a choice.

People are certainly not consistent in preferring 1-indexing. People expect decades and millenia to start on years ending in zero (even though they actually don't, because of 1-indexing).


"There are human (psychological) reasons for that"

Neurological in that the line detecting algorithm is more excited by seeing a triangle of three lines than a horizontal bar of just one line. We have the technology to stick a HDTV on the bumper of each car and replace "brake lights" with a giant glowing stop sign which would work even better. But its basically the same argument.


It's true, even though humans and computers are two very different things, we both share the fact that we are hard wired to do some things (detecting lines and motion in the human case). I guess the discussion above is not the best example since the symbol "1" just some abstract symbol that represents a lot of things (including a unit; and a base for counting), one can just as easily use the symbol "0" for that (although it is counter to the standard notation, and will need some getting used to).

In fact, it would be an interesting neuroscientifical research to measure the neurological activity that the symbol "1" and "1ˢᵗ" trigger in a programmers brain inside the context of a prose or written English language, and compare it to the activity the symbol `a[0]` triggers inside the context of code.


Thank you. Having done a limited amount of assembly programming, 0-indexed arrays seem incredibly obvious, since they allow you to simply add the word size times the index to get the memory address of any entry in an indexed data structure. This is still almost exactly how arrays work in most (compiled) languages.

Personally, I think it's cool that there are elements of mid/high-level programming languages which still reflect very low-level operational concepts.


The reason they seem obvious is that they represent a quantity that has meaning -- the offset.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: