I'm not sure how to feel about only now learning its indexes start at a [1] though (╯°□°)╯︵ ┻━┻
Love it otherwise.
I'm not sure how to feel about only now learning its indexes start at a [1] though (╯°□°)╯︵ ┻━┻
Love it otherwise.
Starting at [0] is not objectively better than [1]. I actually believe it is objectively worse.
When teaching people to program, portraying the idea of “jumping zero steps” into a list to get the first element has been an inefficient embarassment every single time I’ve been present.
Re. Dijkstra’s argument:
1: Everybody has an intuition of an ends-inclusive “from x to y”. Everyone would include 2 and 12 if asked to “count from 2 to 12”.
2: What’s wrong with using three dots? What is pernicious about them?
3: Labeling and length calculations always require a shift of one. If we make the ends compatible with thought-free length calculation, then sequence labeling is always off. If we have intuitive sequence labeling then we need to adjust either end label by one to get the length. The latter approach is far more intuitive. Also, one should just ask the damn computer what the length is. That’s what it’s for. Pardon the language. Especially as we will always want to leave behind and tend to leave behind the abject simplicity of hand-calculating the length of our data from the labels of the bump stops; Our data structures may not even support that calculation. So why couple ourselves to that calculation and sacrifice the ability to have sequence labels make sense?
And why don’t we just have both? Keep array[0] as first, fine, whatever, and add array|1| as first too.
This doesn't seem to be much of an argument so I'm not even sure what to address.
0-based indexing makes sense to me in C where arrays are just pointers and the index is an offset, and doing pointer math is a regular part of the programming experience. But most languages have come a long way from that, and collections such as arrays are much closer to a natural metaphor (a list of things). As such, natural ranges (inclusive) seem more appropriate to me.
julia> x = collect(1:15)'
1×15 LinearAlgebra.Adjoint{Int64,Array{Int64,1}}:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
julia> x[1:10]'
1×10 LinearAlgebra.Adjoint{Int64,Array{Int64,1}}:
1 2 3 4 5 6 7 8 9 10
julia> x[11:end]'
1×5 LinearAlgebra.Adjoint{Int64,Array{Int64,1}}:
11 12 13 14 15
julia> x[11:length(x)]' # alternatively ...
1×5 LinearAlgebra.Adjoint{Int64,Array{Int64,1}}:
11 12 13 14 15The best of both worlds is when a language provides both end-exclusive and end-inclusive slice syntax, as in e.g. Nim: x[0..<10] is end-exclusive, and it's very clear that it is.
Half-open intervals are appropriate when you're thinking of them as subspaces of a continuum. When you're thinking of them as subsequences of discrete elements, closed intervals are usually more natural.
Ruby is the only language I know that really seems to have tried to address the problem, providing different syntax for every way you might want to take a subsequence from an array:
x[5..8] # elements [5] through [8], including [8]
x[5...9] # elements [5] through [9], excluding [9]
x[5, 4] # four elements, starting at [5]
If you were working in pure, pencil-and-paper mathematics, you'd choose whether to index a particular sequence from 0 or 1 based on what made your formula look nicer. Both are common. But that's not an approach I'd suggest for a programming language.IMO, it's better to have dedicated syntax for index-from-end, as part of the range syntax. Again, Nim does that: x[0..^1] (although unfortunately they didn't make it symmetric).
This is true, and note that my suggestion requires it -- the only way to distinguish x[-0] from x[0] is to have the parser do it; -0 and 0 are the same number.