Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does Julia interpret 10:1?

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?

like image 748
MichaelChirico Avatar asked Mar 23 '17 01:03

MichaelChirico


2 Answers

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.

like image 110
mbauman Avatar answered Nov 01 '22 16:11

mbauman


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

like image 5
Gnimuc Avatar answered Nov 01 '22 17:11

Gnimuc