In R this behavior just works fine, I donāt know if it is discouraged in Julia for some reason?
r = seq(1,20,2)
r[8:4]
[1] 15 13 11 9 7
PS: I came across this behaviour trying to index a matrix using 2 ranges to get a sub matrix and sometimes the ranges can be ānegativeā (i.e. starting with the larger number first).
My questions are: Why is Julia currently behaving like this? I especially donāt understand the output of negative ranges always being start:start-1. Is there a better way to do this in base or is there a package that can be used to enable this kind of behaviour?
(a::Int):(b::Int) creates a UnitRange, which has a step of +1 by definition:
help?> UnitRange
search: UnitRange AbstractUnitRange
UnitRange{T<:Real}
A range parameterized by a start and stop of type T, filled with elements
spaced by 1 from start until stop is exceeded. The syntax a:b with a and b
both Integers creates a UnitRange.
The negative range endpoint coercion allows the length of a UnitRange to always be equal to stop - start + 1, which avoids an unnecessary branch in subsequent calculations of length. If you want to create StepRanges with a unit step of the proper sign, you could do something like this:
julia> ..(a, b) = a:sign(b - a):b
.. (generic function with 1 method)
julia> 4..8
4:1:8
julia> 8..4
8:-1:4
**edit: this is more robust (works for a == b):
julia> ..(a, b) = a:sign(b - a + (a == b)):b
.. (generic function with 1 method)
julia> 2..2
2:1:2
The negative range endpoint coercion allows the length of a UnitRange to always be equal to stop - start + 1 , which avoids an unnecessary branch in subsequent calculations of length .
So this refers to the compiler having to do less work with the current implementation?
Thanks for the nice explanation though. Your proposed solution is great, I will implement it right away.
I am still a bit curious as to why for ranges start:stop with start < stop currently a range of start:start-1 is returned? Is there a particular use case for this? Would an error not more desirable, because otherwise silently an empty collection is returned which may lead to unintended behaviour?
yes, so when you do a loop over ends points, you get the correct behavior (empty, 0-length, skip it) without manually worrying about which number is larger.
Julia implemements short cuts (specialized methods) that make sense, for example:
Thereās also the issue of type stability, which is so central in Julia (and in particular for a commonly used construct like UnitRange). The step length of +1 is encoded in the type itself. If stop<start caused the step to become -1, it would no longer be a UnitRange, and when output types depend on values, you have a type instability.