6

I'm an expat from focusing on R for a long time where the : (colon) operator creates integer sequences from the first to the second argument:

1:10
# [1]  1  2  3  4  5  6  7  8  9 10
10:1
# [1] 10  9  8  7  6  5  4  3  2  1

Noticing that this appears to work the same in Julia, I ran into an error:

1:10 .== 10:1

DimensionMismatch("arrays could not be broadcast to a common size")

Because:

10:1

Outputs

10:9

I'm puzzled about how this could have happened. It seems quite natural not to need to use 10:-1:1 -- why did Julia think 10:9 was the right interpretation of 10:1?

Shreyos Adikari
  • 12,348
  • 19
  • 73
  • 82
MichaelChirico
  • 33,841
  • 14
  • 113
  • 198

2 Answers2

14

Julia is not R. There are other languages which have a similar interpretation for the colon syntax as Julia. MATLAB treats 10:1 as an empty array and Python's slicing syntax (while different in other ways) also treats indexing with 10:1 as an empty selection. Julia chooses to normalize empty integer ranges such that the difference between start and stop is always -1, so it becomes 10:9.

So I don't think there's an unambiguously obvious interpretation of 10:1. There are, however, some very conducive arguments for Julia's interpretation in my view:

  • The empty range 10:9 is used to represent the location between indices 9 and 10 in some APIs.

  • Ranges are a core construct in Julia and for x in 1:10 absolutely and unequivocally must be as fast as the equivalent C loop. Because the syntax x:y always increments by one (and never negative one), Julia (and LLVM) can take advantage of that constant when compiling for loops to enable further optimizations. Making this not constant --- or worse, dynamically switching between UnitRange and StepRange depending upon the values of the end points would thwart this optimization or be type-unstable.

  • Personally, I find R's interpretation just as surprising as you find Julia's. I'd argue that the need to be explicit that you want a step of -1 is advantageous in both readability and bug prevention. But I acknowledge that my experience with prior languages is just as biased as yours is.

Graham
  • 7,431
  • 18
  • 59
  • 84
mbauman
  • 30,958
  • 4
  • 88
  • 123
  • I would put the optimization reason first and foremost -- it's the _sine qua non_. Thanks for the insight; I had a sneaking suspicion there was an optimization-over-robustness argument in play. – MichaelChirico Mar 23 '17 at 03:03
  • 2
    I don't think that's optimization vs. robustness, really. It's optimization and robustness. Coming from R, I quite like Julia's behavior. In R, when you write complex code where the endpoints come from other computations, you often get bugs because you didn't explicitly handle the case where the starting value is higher than the ending one. An empty range is actually what you want in most of these cases. – Milan Bouchet-Valat Mar 23 '17 at 17:45
  • 1
    @MilanBouchet-Valat fair enough; I can't say I haven't been hit by that type of bug in R. – MichaelChirico Mar 23 '17 at 17:58
5

In Julia, we assume a:b constructs a range with a step size of 1, so 10:1 is an UnitRange which is supposed to be an empty range. Since a:a-1 is also an empty range, it's equivalent to a:b where b<a, please take a look at the source code here.

julia> dump(10:1)
UnitRange{Int64}
  start: Int64 10
  stop: Int64 9

julia> dump(10:-1:1)
StepRange{Int64,Int64}
  start: Int64 10
  step: Int64 -1
  stop: Int64 1

Here 10:-1:1 is a StepRange with a step size of -1, which, I think, is more accurate and natural to represent the idea of "10 to 1".

Gnimuc
  • 8,098
  • 2
  • 36
  • 50
  • Is there any justification of this strange behavior? I suppose it's that the object `a:b` creates is of a certain class that requires `a – MichaelChirico Mar 23 '17 at 02:42
  • @MichaelChirico the [doc](http://docs.julialang.org/en/latest/stdlib/math.html#Base.colon) clearly says this behavior(`a:b`<=>`a:1:b`). Not to mention that, among Matlab, Julia, Python and R, only R interprets `10:1` as `1:10`. – Gnimuc Mar 23 '17 at 03:19
  • Being new to Julia, I don't know how I could have found the documentation for `:` -- is there something like `?":"` which I use in R? Also, I find the documentation misleading, as `a:b` is NOT equivalent to `a:1:b`. `a:b` creates a `UnitRange` object; `a:1:b` creates a `StepRange` object. – MichaelChirico Mar 28 '17 at 03:06
  • @MichaelChirico sorry, you're right. It seems there is only a `colon` function in the doc. I usually use `apropos()` function to search related docs. (e.g. apropos(1:2) ) – Gnimuc Mar 28 '17 at 03:19
  • Hmm. `apropos(1:2)` doesn't return anything about `colon` for me – MichaelChirico Mar 28 '17 at 03:20
  • 1
    @MichaelChirico then `apropos("a:b")` works in julia-v0.5, `apropos(1:2)` works in v0.6. – Gnimuc Mar 28 '17 at 03:23
  • or just type `"a:b"` in help mode ;) – Gnimuc Mar 28 '17 at 03:26