I initially defended the choice, but I now agree that 1-based indexing now seems like a poor choice since Julia has become something more than the original mission of a better MATLAB or Octave. It’s a, admittedly, minor tragedy of Julia’s success.
I initially defended the choice, but I now agree that 1-based indexing now seems like a poor choice since Julia has become something more than the original mission of a better MATLAB or Octave. It’s a, admittedly, minor tragedy of Julia’s success.
>In order to read a csv in that doesn't have a header and for only certain columns you need to pass params header=None and usecols=[3,6] for the 4th and 7th columns:
https://stackoverflow.com/questions/29287224/pandas-read-in-...
Just reading that hurts me.
Again, 0-based indexing exists to fit a purpose: http://www.cs.utexas.edu/users/EWD/transcriptions/EWD08xx/EW...
In my opinion, reading `a = b[1:n-1]` hurts much more than reading `a = b[:n]`.
No, that's their ordinal position, and how 8 billion non-programmers would refer them as in any everyday setting.
That's also how programmers would refer to them if it wasn't for a historical accident.
What's more, that's also how programmers refer to them when they talk between then and not to the machine ("check the 3rd column" not "check the column at the index of 2").
I have taught Python quite a bit, and I have gotten good at explaining 0 based indexing and slicing based on it. When I switched to Julia there was nothing to explain. And my code has about as many +/- 1s as before...
These spoken language conventions developed before there was an established name for “zero” or even a concept that “nothing” could be a number per se.
For similar reasons, we have no zero cards in our decks, no zero faces on a dice, no zero hour on our clocks, no zero year in our calendar, no zeroth floors in our buildings, East Asian babies are born with age one year, etc.
It’s only by another set of historical accidents that we have a proper 0 in our method of writing down whole numbers. Thankfully that one was of obvious enough benefit that it became widely adopted.
In (North?) America. In Europe, there's a ground floor (zero), then first floor (1), etc. Basement is -1 (etc.).
A European friend of mine arrived at college in the USA, and was assigned a room on the first floor of the dorm. She then asked the housing office whether there was a lift, because she had quite some heavy luggage, earning some rather amused looks :-)
Whoever designed the European convention for labeling building floors was numerically competent.
Too bad medieval European mathematicians and the designers of Fortran weren’t. ;-)
I'm happy to be writing
``` for i in 1:n func!(a[i]) end ```
to iterate over an object of length n. Or split an array as a[1:m], b[m+1:n]. Slicing semantics which are far more prevalent in my code (and the code I read) than index arithmetic, are truly vastly simplified by 1 based indexing of Julia compared to the 0 based numpy conventions. We simply no longer code in the world that Dijkstra argued for, and I have not seen anybody give a clear argument that is actually rooted in maths and contemporary programming.
I genuinely thought that the Python convention was brilliant, and that 1-based indexing in Julia would suck. It turned out not to be the case.
I am legitimately (mildly) curious about the history of the different naming conventions for floors of buildings though.
> The base level doesn't need to have a floor, it's just ground. Once you add a floor you are on the first floor above ground.
Yes, my point is this is an example where the European 0-based indexing system makes more sense (in my opinion) than the American 1-based indexing system. I speculate that whoever started calling the ground floor the “first floor” hadn’t really put much thought into how well that would generalize to large buildings with many floors including some underground.
Similarly, whoever decided the calendar should start at year 1 AD with the prior year as 1 BC hadn’t really considered that it might be nice to do arithmetic with intervals of years across the boundary.
There are many standard mathematical formulas which are clarified by indexing from 0. But nobody can switch because the 1-indexed version is culturally fixed. Most of the rest of the time the 0-indexed vs. 1-indexed versions makes basically no difference. It is rare that the 1-indexed version is notably nicer.
> Or split an array as a[1:m], a[m+1:n]
Yes, I find it substantially clearer to write this split as a[:m], a[m:]. Particularly when dealing with e.g. parsing serialized structured data. But also when writing numerical code. Carrying the extra 1s around just adds clutter, and forces me to add and subtract 1s all over the place; reasoning about it adds mental overhead, and extra bugs sneak in. (At least when writing Matlab code; I haven’t spent much time with Julia.)
There is. We call it 12 for some crazy reason (it goes 12 AM, 1 AM, 2 AM, ..., 11 AM, 12 PM, 1 PM, ...).
> no zero year in our calendar
Which is quite irritating really. New Year's Day 2000 wasn't the start of the 3rd millenium, because there was no year zero.
> East Asian babies are born with age one year
But not western babies.
I don’t know the history of reported ages of Western babies.
> quite irritating really
Yes that is my point.
I’m curious as to why this is a problem outside numerical computing. From my perspective, this is consistent with a long history of mathematics dealing with matrices that predates electronic computers.
0-based arrays are popular because C decided to deviate from what had previously been standard in math and in Fortran.
Is there a reason other than aesthetic preference and habit that makes 0-based indexes better for computing in non-numerical contexts?
I realize both indexing standards are arbitrary and boil down to “that’s the way grandpa did it,” however 1-based indexing grandpa is way older and more entrenched outside computing circles.
EDIT: I suppose with Julia it’s not that important, as other commenters have pointed out that you can choose arbitrary indexes.
I also thought I'd seen a longer text focusing more on the counting/indexing.
I still don't see the appeal of "element at zero offset" vs simply "first element".
I do agree that < vs <= etc can get messy. But outside of now fairly archaic programming languages I don't see the need. Just use a construct that handles ranges, like "for each element in collection/range/etc". (Or for math, "pattern matching" (or "for n in 1..m").
An unexpected takeaway: Michael Chastain's time-traveling debugging framework, which he had in 1995, and which still reads like sci-fi from the future[1].
Alas, this quote will probably stay relevant for a while:
>[We keep] finding programming constructs, ideas and approaches we call part of “modern” programming if we attempt them at all, sitting abandoned in 45-year-old demo code for dead languages.
Then the second parts quotes the creator of BCPL, revealing – a-HA! – what we already knew, that the array is a pointer to the first element and the index is the offset from the pointer, and that's why it's zero.
(And after that it veers of into complaining about how the papers documenting this history cost too much money, so they didn't actually read them.)
People argue that zero-based is incidental, and that 1-based is the right way because of it's long history in mathematics notation. I would argue that 1-based is incidental, and that zero-based is better most of the time for modern math and computer architectures.
Whatever your answer, I suspect it is more cognitive overhead to remember than "always 1 based" or "always 0 based".
Depends what the library function does of course. If it's shape preserving (e.g. if it's a map over the array), it'll generally preserve the axes of the input array.
> What if I do an outer product of two arrays with different base indices?
You'll get an offset array indexed by the product of the axes of the input:
julia> A = OffsetArray(1:3, 0:2)
OffsetArray(::UnitRange{Int64}, 0:2) with eltype Int64 with indices 0:2:
1
2
3
julia> B = 4:6
4:6
julia> A*B'
OffsetArray(::Array{Int64,2}, 0:2, 1:3) with eltype Int64 with indices 0:2×1:3:
4 5 6
8 10 12
12 15 18
> Whatever your answer, I suspect it is more cognitive overhead to remember than "always 1 based" or "always 0 based".Sure, but the real point here is that in most situations, you don't actually care what the axes are, because you use the higher level abstractions (e.g. iteration over elements or the index space, linear algebra operations, broadcasts, etc.), which all know how to deal with arbitrary axes. The only time you should ever really have to think about what your axes are is if there's some practical relevance to your problem (OffsetArrays are used heavily in the images stack).
This points to another example where 0-based should be preferred. When doing modulo arithmetic, 0..N-1 mod N gives 0..N-1, but 1..N mod N puts the zero at the end. I also cringe at languages where -1 mod N does not equal N-1.
For any language with a mod operator, a mod b should always be equal to (a + k×b) mod b for any integer k.
Breaking this invariant makes the mod operator useless in pretty much every application I ever have for it. In e.g. JavaScript I need to define a silly helper function like
mod = (x,y) => x%y + y*(x%y && x>0^y>0)
And then remember to never use the native % operator.The other (“remainder”) version which takes its sign from the first argument is pretty much worthless in practice. IMO it doesn’t need any name at all. But what it definitely doesn’t need is a shorter and more convenient name than the useful modulo operator. Its ubiquity is a serious screwup in programming language design, albeit largely accidental.
In my opinion neither are used sufficiently often to justify taking up a valuable ASCII symbol (same with xor).
I don’t know what is available as chip instructions, I could certainly believe hardware designers made the wrong choice.
[0] http://www.lispworks.com/documentation/lw50/CLHS/Body/f_floo...
[1] http://www.lispworks.com/documentation/lw50/CLHS/Body/f_mod_...
As one simple example, it is frequently useful to map floats into the range [0, 2π), but I have never once wanted to map positive floats into the range [0, 2π) while negative floats get mapped into (–2π, 0] by the same code.
- https://github.com/JuliaLang/julia/issues/14826
mod(-eps(), 4.0)
- https://github.com/JuliaLang/julia/issues/17415 fld(1.0,0.2)
- https://github.com/JuliaLang/julia/issues/3127 mod(1.0,0.1)
- https://github.com/JuliaLang/julia/issues/3104 mod(0.95,0.01)
I'm pretty sure there are more issues.re: mod 2pi, typically it is most accurate to reduce to (-pi,pi) (i.e. division rounding to nearest). Also to get it accurate you need fancy range reduction algorithms, hence julia has rem2pi https://docs.julialang.org/en/stable/base/math/#Base.Math.re...
These examples don’t really have much bearing on the general usefulness of “remainder” vs. “modulo” though.
That's precisely what it does!
> I also cringe at languages where -1 mod N does not equal N-1.
julia> mod(-1,10)
9
But admittedly when you use 1-indexed arrays, any kind of modulo operator becomes pretty inconvenient a lot of the time (lots of futzing to avoid off-by-1 or boundary errors). So maybe it doesn’t matter in the Julia context.
1-based indexing has the advantage of always knowing the length of the array since the index of the last element is this value. You also get "proper" counting numbers as you iterate.
I've used both in various languages. Perl lets you define the array index start to be whatever number you wish.
Especially since it would have been very easy to 'do it like Ada' and allow any start index by default (I have use Lua and it's really annoying to use an extension language with 1-index when the base language is 0-indexed)
Until then Fortran, Lisp, Algol, Pascal, Basic never had any big issue with indexes.
Programming languages should not be configurable in that kind of way.
For a pre-baked solution: https://github.com/JuliaArrays/OffsetArrays.jl
Instead of complaining in the abstract, check it out, you'll be impressed. https://julialang.org/blog/2017/04/offset-arrays
Default matters!
(not referring specifically to you here!)