top | item 10110976

Indices Point Between Elements

133 points| nelhage | 10 years ago |blog.nelhage.com

62 comments

order

thwarted|10 years ago

The visuals are definitely valuable in explaining this.

It used to be popular, and still is in some circles, to debate whether programming languages ought start array indexing at 0 or 1.

When talking about this with other programmers, I've discovered that a lot of the issues/confusion could be avoided by consistent use of terminology: Offsets/offsetting always being zero-based and indexes/indexing always being one-based.

Using rulers and birthdays also helps to explain differences. You're in the first year before your first birthday, being zero (whole) years old.

To make matters potentially more confusing, culturally, I remember something about the ground floor in the UK buildings not being "Floor 1" like it is in the United States.

http://www.hacker-dictionary.com/terms/fencepost-error

duaneb|10 years ago

I would argue an index, in the sense that it might be an integer, is indistinguishable from an offset. I would refer to one-based indexing as positional—1st, 2nd, 3rd, ..., ith.

Of course, it's the offset from the first element, so it's kind of a circular definition.

pavlov|10 years ago

I remember something about the ground floor in the UK buildings not being "Floor 1" like it is in the United States.

Actually, that's perfectly explained with your offset vs. index terminology. In some countries, the floor number is an index within the array of floors. In others, it's an offset from the ground.

cjhveal|10 years ago

The birthday analogy is a good one, I'll have to keep that in mind. Your actual "birthday" being the points between year elements, your "age" as the offset from birth.

As to the point about floor numbering, the situation can be a little confusing in Canada. Some buildings label in the US style, labeling stories [1st, 2nd, 3rd] while others label in the UK style as [Ground, 1st, 2nd]. We also often mix them and you'll see [Ground, 2nd, 3rd], with ground sometimes replaced by main, or lobby.

SerpentJoe|10 years ago

I prefer that model for floor numbering, personally. It means if you're on floor N that you're N stories above the ground.

derefr|10 years ago

> You're in the first year before your first birthday, being zero (whole) years old.

I would argue that your "first birthday" is, in fact, the day you are born—your birth day. The thing that happens for the first time a year later, is the first anniversary of your birth day.

LoSboccacc|10 years ago

_It used to be popular, and still is in some circles, to debate whether programming languages ought start array indexing at 0 or 1_

this is an exemplary case of citation needed if I ever saw one. maybe it's a valid debate for programming languages that doesn't allow people to do pointer arithmetic, which already restrict the field a lot, but even then that's sound as part of the 4GL bullshit that never really took off, and for good reasons

surrealize|10 years ago

People have taken different approaches to this in bioinformatics for numbering intervals of dna bases in chromosomes. I think this approach is catching on, though. In that realm, it's important to speak unambiguously about insertions and deletions, and the "interbase" mental model makes it a lot clearer.

edit: Probably the most popular genome browser, based at UC Santa Cruz, uses this zero-based, half-open numbering internally. But, at some point in the past, biologists developed an expectation that numbering would be 1-based. So the Santa Cruz genome browser actually adds 1 to the start position of everything for display purposes.

rcthompson|10 years ago

One interesting side-effect of using 1-based closed indexing (i.e. numbering the positions, not between the positions) is that a zero-width range (which is something that actually comes up in genomics) starts at position N and ends at N-1.

carapace|10 years ago

Once at a party I accidentally started an argument about which way toilet paper should hang from the roll (front or back) by mentioning how silly it was that people would argue about such a trivial matter.

leni536|10 years ago

Also at which and do you start pealing a banana?

sago|10 years ago

I've found this a helpful way of explaining ranges in Python to students, it also makes Python's negative indices understandable for the same reason.

But it is contextual. When it comes to languages like C, where arrays are more directly mapped to pointers and memory layout, I've found it better to talk about pointers, and allow people to derive the behavior that way.

Either way, I'd be careful of trying to claim that 'this is what it is', rather than 'here is a way to remember it'.

nelhage|10 years ago

I get into this a bit later on, but I think the exact same model applies to pointers: You're much better off in most cases thinking of pointers as pointing at the zero-width points between elements, than at elements themselves.

orclev|10 years ago

He undermines his own argument right out of the gate. When he shows the part about which number should be used for the last element in the range there's still two valid choices, you could choose to stop at 3 with the understanding that the element to the right of that is the last element to be selected, or to stop at 4 with the understanding that in this context your indexing is "special" and you're actually selecting the element to the left of the index.

morpher|10 years ago

If you treat the indices as between elements and include all elements that are between the start and end elements, there is only one reasonable choice. It would be more "special" to include elements that are outside of the specified range (i.e., the element to the right of the final index).

perlgeek|10 years ago

When you talking about where zero-width regexes match, this mental model certainly helps, and I find it consistent otherwise too.

nelhage|10 years ago

zero-width matches (and empty lines) were a huge source of stupid edge-case bugs in livegrep[1], and being rigorous about maintaining this mental model definitely helped a lot.

[1] livegrep.com

bch|10 years ago

There is a reference to Emacs in the OP, but not directly to what immediately sprung up in my mind[0]. In Emacs, it has a cursor (the block) and a "point" which is described as an imaginary spot at the left edge of the cursor - the point is what's important when one is indexing data (ie: setting a mark and selecting a bunch of text). The model fits for lots of things... Unfortunately for me, tmux highlighting does not follow this model.

[0] https://www.gnu.org/software/emacs/manual/html_node/emacs/Po...

edit: clarify Emacs reference in OP

MrManatee|10 years ago

I have sometimes wondered whether it would be useful to have two different types for indices, depending on if we are indexing the elements themselves or the "gaps" between them. Let's say I'm thinking about some language like Haskell, where types are already a big deal.

That way the compiler would be able to tell if I accidentally mixed the two. Every conversion would have to explicit: for example, there might be two functions, "before" and "after", that take a gap index, and return an element index.

I think I might actually enjoy programming this way, but perhaps others would find it needlessly bureaucratic.

marijn|10 years ago

And this is why JavaScript's `lastIndexOf` method gets it wrong, wrong, wrong. (`"foo".lastIndexOf("o", 2)` yields 2, whereas I, and probably a lot of other people, would expect it to be 1, with the search starting at the space between element 1 and 2)

Camillo|10 years ago

This is the mental model I use.

colanderman|10 years ago

This is the same model used by the Cairo graphics library and the HTML5 Canvas (albeit in two dimensions). All the same benefits apply when you're addressing pixels as elements of an array.

ape4|10 years ago

We need a different name for this. We're too used to "index". If the name is "Foo" then we can have fooSubString(i,j);

pshc|10 years ago

This echoes the way unums work in "The End of Error". (Recommended read by the way.) Good way of thinking about ranges.

amelius|10 years ago

Ugh. Please no.

This only adds to the confusion, as now there are 2 ways in which indices can be interpreted.

Retra|10 years ago

Specifically, this:

>"Indexing between elements, instead of indexing elements, helps avoid a large class of off-by-one errors."

It only replaces them with indexing-method errors. Instead of remembering if my ranges are open or closed, I have to remember if they are using between-element indices or on-element indices. It's still going to cause the same kinds of problems.

LoSboccacc|10 years ago

also this works only in this special case of array being one sized.

an index is an offset to a pointer in memory, shifted by the size of the structure it points to. there is no other way around, no magic tricks about index being between elements.

of course people never exposed from c miss out all of this, and then are left to made up bullshit about how stuff actually works

array index are offset to a memory location, plain and simple; if that requires more explaining than this, then there is the need to get back to tech the basic underpinning on how computer memory and addressing works

maaku|10 years ago

Great teaching tool, thank you.

anon5446372|10 years ago

Some people spend too much time overthinking simple concepts...

kmill|10 years ago

Some people overestimate the simplicity of common concepts...