Hacker News new | ask | show | jobs
by FullyFunctional 1062 days ago
Once you flatten multidimensional arrays the folly of starting at 1 becomes apparent (much like mathematics got so much simpler when we switched to logarithms with the "obvious" log a + log b = log ab which was a breakthrough at the time).

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.

3 comments

> 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]

> 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.

Or we can make programming languages that do not require flattening by hand. Off-by-ones are easy to make in both systems. Also it’s annoying that standard libraries do not include functions like n_ints_inclusive(from, to), range_of_last_n_or_less(arr_size, n), n_elems_before(range, n), index_after_range(range), flatten2(i, j, stride) and so on. As if PL designers wanted us to draw ranges on paper and visually check our reasoning every time we have to deal with indices.
In fact the vast majority of algorithms which involve doing computation on indexes beyond increment/decrement are naturally implemented with zero-based indexing. When using ones based indexing you end up having to convert to zero based, do the computation, then convert back to one based.

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.

> 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?

Great, now you've messed up all the other math that uses the modulo operator. The mathematical operators behave the way they do for well established reasons that long predate the invention of computers. It's going to be a tough sell to get everyone to adopt a wholesale refactoring of modulo arithmetic (and likely number theory in general) just for the "convenience" of one based indexing.