Edsger Dijkstra's note on starting array indices at 0 (pdf, 1982)
cs.utexas.edu
cs.utexas.edu
In every non-computer related document one will found ordinal numbers starting from 1 and n-element sets with the number of the last element being n. Now thanks to the zero-based indexing one has to always switch back and forth between two ways of counting things, depending if one writes/reads code or prose. Just pay attention over the next week or month how many times you forget to make the switch and make this kind of off-by-one mistake I'm talking about. I admire mathematics more then any other discipline and I do see the benefits of a formal approach, but here the formalism has in my opinion clearly diverged from any kind of common sense and from actual software engineering practice.
In fact in any engineering discipline, if you study its history, communication errors are the most frequent reason for failure or even for disaster. Having to always translate every document to the private language of your discipline certainly doesn't help communication. Dijkstra mostly did proofs and didn't have to deal with other people too much in his work, so perhaps he didn't understand that.
"Are you sure?" said his wife. "It looks like they are still all here."
"There are only eleven left. I've counted them a dozen times at least. I'll count them again. See: zero, one, two...."
P.S. The most famous example I can think of is the aleph notation for the infinite cardinals, which starts with aleph null: http://en.wikipedia.org/wiki/Aleph
However, whether we start from 0 or 1 is ultimately arbitrary and more a question of symbol choice than semantics, since the sequence of natural numbers beginning with 1 is isomorphic to the sequence of natural numbers beginning with 0. Set-theoretically it makes a little more sense to start with 0, but when considering arithmetic alone, it really makes no difference.
It feels like this is related to the question of whether the "natural numbers" includes zero or not. In the set theory classes I took, we used N (in "blackboard bold") for the natural numbers 0, 1, 2, 3, ... and Z+ (in "blackboard bold") for the positive integers. My professor's pragmatic take on the controversy was that Z+ already means the positive integers, so it seemed like a waste to let N mean the same thing as Z+ and have no symbol to denote the nonnegative integers.
The natural numbers are how we count things. How many moons does Earth have? One. How many eggs are in the carton in my fridge? Twelve. How many elephants are in this room? Zero.
Incidentally, all of my professors agreed with this and most of them used 0-indexing unless some insight or ease of work was exposed by a different indexing scheme.
BTW, there is an important point made in your comment, one I had to hammer on repeatedly when I taught discrete math: that the answer to a "How many ... ?" question is a nonnegative integer, i.e., an element of {0, 1, 2, 3, ...}. I've found that this fact is surprisingly difficult for many people to grasp.
There's an advantage to starting from zero if you want to work with Taylor expansions.
So yeah, sure, he could make a mistake, but the premise of the joke is that such a mistake is especially likely for a mathematician counting from 0, and I don't think it is.
1. The mathematician's wife was not using ordinal numbers; nor was he. They were both asking "how many of these things are there?". Cardinals, not ordinals.
2. A mathematician who's so used to 0-based working that he always counts from 0 simply will not interpret "0,1,...,11" as meaning that there are 11 things. The same unusual way of looking at things that makes him start from 0 will also make him proceed from "...,10,11" to "there are 12 things".
3. The mathematician's wife didn't "ask for 12". She left him with 12 packages. What happens in the joke is not a miscommunication, it's s slip-up by the mathematician in going from "...,10,11" to "12 things". Which I suggest is highly implausible for a mathematician in whom 0-based counting is deeply ingrained.
And now I've had enough of overanalysing this joke and shall leave it alone. Feel free to have the last word if you wish.
Incidentally, (some) mathematicians prefer their ordinals to start from 0 as well. The trouble is that "first" (derived from "foremost") really ought to mean the one you start with rather than the one numbered 1, and "second" (from the Latin "secundus" meaning "following") really ought to mean the one after that, and "third" etc. are derived from the number names and therefore have to correspond to 3,4,... -- so there's a gap at 2. One of my acquaintances therefore likes to use a sequence of ordinals that goes: first (0th), second (1nd), twifth (2th), third (3rd), fourth (4th), fifth (5th), etc. I don't think it'll catch on.
In languages without foreach or map I'd guess it's one of the most frequent operations. You probably work with the wrong kind of code to appreciate just how awful anything but the [0..n) behaviour is for... I'm sorry, it's so pervasive I can't even come up with a good example. The most trivial thing is offseting an array by an index by addition.
Even if we assumed that one-based indexing was a good thing, many languages -VB prominently- can't seem to make their mind and use a single convention through: arrays may be one-based, but collections are 0-based, and when you start having to interface with libraries written in other languages or APIs, you're left walking on eggs, always wondering if you're not off by one somewhere.
FWIW I grew up on FORTRAN which starts from 1 and work with PL/SQL now which also starts from 1, so I am used to it, but I understand why it makes sense to do it from 0 in terms of the way computers actually work. At least, computers with operating systems written in C.
x % y
When either x or y is negative, or both (the full details are described at Wikipedia: http://en.wikipedia.org/wiki/Modulo_operation). In common mathematical notation x mod y, the result always shares the sign of the divisor y. For example,–14 mod 10 = 6
6 mod –10 = –4
The same is the case in python:
>>> -14 % 10
6
>>> 6 % -10
-4
Unfortunately, in JavaScript (and various other languages), a different convention is chosen. The result instead shares the sign of the dividend: > -14 % 10
-4
> 6 % -10
6
This is really annoying because we quite frequently want to use the modulus operator to do things like normalize an angle to the range [0, 360°). To perform this normalization on an angle a in JavaScript, if a < 0, requires a computation like: a_prime = a % 360 + 360
I end up usually defining a function like: var mod = function (x, y) {
var js_mod;
return (js_mod = x % y) && (x > 0 ^ y > 0) ? js_mod + y : js_mod;
}A convenient property to have that is held by the Python version is (for nonzero n)
x==n*floor(x/n)+x%n
This property isn't held by the Javascript variant, where there is a somewhat more complex property x==n*sign(x/n)*floor(abs(x/n))+x%n x == n * (x // n) + x % nIt's worth noting that x//n returns an int and math.floor returns a float, so the statements aren't equivalent.
The `floor' operation -- that truncates toward negative infinity -- is a more useful primitive, and goes hand in hand with the modulo operation defined as you describe.
(mod -1 5) => 4 (mod 13 4) => 1 (mod -13 4) => 3 (mod 13 -4) => -3
Generally any time array indexing intersects with mathematics you are going to get the same issue.
That combined with the fact that, in Lua, sometable[0] doesn't produce a not-present exception (it returns 'nil') resulted in a lot of unnecessary cursing.
However, according to section 3.1.2.1 of the Mesa language manual [1], that was the notation used by Mesa, and according to Dijkstra, it didn't work. It caused errors and confusion. I can guess why -- in mathematics, the braces are made large enough to be visually prominent around the contents. In a line of code all rendered in the same size font, they would be easy to lose and would not hit your subconscious mind the way they do in a mathematical expression.
Someday, with better code rendering in editors, maybe we can just use the appropriate braces to indicate whether interval bounds are inclusive or exclusive.
[1] http://research.microsoft.com/en-us/um/people/blampson/23a-M...
Of course, if it was indexed from 1 to N, then (i % N) + 1 would do the trick, but that's more cumbersome and uglier for what can be a frequent operation.
1 -> 2
2 -> 3
3 -> 4
4 -> 5
5 -> 1
6 -> 2
7 -> 3
8 -> 4
9 -> 5
10 -> 1
...
This is a valid mapping from Z+ to our index space. Is your objection that 2 does not map to 2, 3 does not map to 3? That may be a useful property, but (i % N) + 1 is still a valid mapping without that property."Should array indices start at 0 or 1? My compromise of 0.5 was rejected without, I thought, proper consideration." -- Stan Kelly-Bootle
So yeah, the array would start at 0,5.
$ echo '0123456789' | tail -c +5
456789 $ tail --version | head -n 1
tail (GNU coreutils) 8.5
$ man tail | grep -A 2 bytes=K
-c, --bytes=K
output the last K bytes; alternatively, use -c +K to output
bytes starting with the Kth of each file
The fifth byte of '0123456789' is the byte corresponding to '4', and the manpage says it will output bytes starting with the 5th (ie 456789)A good analogy to this way of thinking is a person's age. When a person is born, or in other words when a person is at the beginning of the array of years that he/she exists, the age index is 0. At the person's first birthday, the age index is 1. Then 2, 3, and so on.
I guess the tl;dr then is that I agree with Dijkstra that array indices should begin at zero.
HOWEVER, when you are doing math, you more often think in terms of sets of cardinality N, with N=0 being an empty set. Also, the last 400 years of math has been written with one-indexing, so when you translate matrix formulas or summation notation into numpy, you have to dork around with -1 all the time, and it sucks. Matlab and R are both 1-indexed and it is the only thing that really makes sense when you are working with them.
I personally hate one-indexing, but 98% of what I do is data analysis and mathematical programming. If I was doing low-level (-ish) memory addressing, it would be a different story.
I thought Perl and Tcl and Python and Ruby just adopted zero-indexing by default from C, but now I see someone smart advocated for it explicitly (too bad he is wrong, at least part of the time)
Any language which does not facilitate this, should be cast from The Bridge of Death into the Gorge of Eternal Peril.
http://www.cs.utexas.edu/users/EWD/transcriptions/EWD08xx/EW...
Direct download of TrueType font at http://fonts.goldenweb.it/download2.php?d2=Freeware_fonts...
The fact that for(int i = 0; i < length; i++) is nicer than for(int i = 1; i <= length; i++) is irrelevant. The c style for loop is designed to make the complex case easier while making the common case harder. It also makes some compiler optimisations impossible
A better for loop would be something like for(int i = start to end)
However with 0 based indexing you end up with e.g in delphi:
for i := 0 to count-1 ... which is an open invitation to error.
In other words, the for loop construct should not be used as an argument for or against 0 based indexing, it should be derived from the indexed used.
And the proper for loop is "for x in n".
A list of twenty items has a twentieth item.
Dijkstra is far too mathematical with this―no student learning programming for the first time (edited: changed from programmer) in the world ever thought "arrays starting at 0―how natural!".
Better than starting at pi; that would be irrational.
A clock starts at zero. A day has 24 hours but there is not a twenty-fourth hour. Midnight is 0:00.
"This structure tracked a 24-hour day using both sundials and mechanical hour indicators"
You're right that it is convenient that the address of an item is first index + current index * index size in memory, but things like that comes at a cost of making indeces completely unintuitive.
That is because they are learning it for the first time.
Another way of putting that might be that they don't know a single thing about the subject ;)
As an example, think of Dijkstra's obsession of using logical proofs of correctness (and not tests) as a way to assure software quality. It is also strictly correct but simply impracticable.
Sometimes an incorrect but intuitive abstraction is better than a correct but harder to grasp, even if just a little harder. These are cases like that.
1. You weren't writing the software in a portable high-level language, but in assembly or machine code. Consequently a new machine was running new software, or simulating an old machine (very slowly). Waiting until the new machine was ready to begin writing the software (assemblers, simulators, compilers, OS) would have enormously delayed delivering the machine to customers.
2. Computers, and thus tests, ran a lot slower. Logical proofs of correctness ran at almost the same speed. Consider the System/360 Model 30: http://www-03.ibm.com/ibm/history/exhibits/mainframe/mainfra... --- 34500 32-bit-word additions per second, compared to 10 billion or so on the machine sitting on your desk, not counting the GPU or possible SIMD instructions. A test that would take your machine a millisecond to run would take the Model 30 about 5 minutes. This also meant that the cost of a bug that necessitated re-running a big compute job was substantial, even if it was caught and didn't produce any worse results than a resubmission.
(There are, of course, proofs of correctness that we can do today with computer help that were not practical then; but those were mostly not what Dijkstra was considering.)
3. Computers mostly weren't interactive. Timesharing was being born, but it hadn't taken off yet, and there were only a small number of computers dedicated to the usage of a single person, or on which you could reserve single-person time. This means that the speed problem is actually much more severe than it appears --- your 1-millisecond test might take 5 minutes to run, but you have to put it in the job queue, and you'll get the result probably within 24 to 48 hours --- and also that the most valued properties of your program are things like "correctness" and not "usability" or "fun". (Usability was actually already important in avoiding user errors and associated incorrect results and wasted computer time, but most people didn't realize this yet.)
4. Computers and support software were much simpler. (And, to hear the old-timers tell it, much more likely to behave as specified.)
Despite all this, I think that some of Dijkstra's emphasis on structuring software so that its correctness is easy to verify is part of the mainstream today, and it's essential in making the software testable. Take a look at The Elements of Programming Style and compare the programs they quoted from textbooks to almost any modern code.
1 to: 5 do: [ " ... " ]
(The ones about proof strategies are good too, and those where he laments the quality of the Dutch higher educational system, though I imagine these are less interesting to computer scientists outside the Netherlands. It never seizes to amaze me how much of his writings are still relevant decades later.)
Unfortunately I didn't have the presence of mind to create my daughter a zeroth birthday cake until a few days after the event.