# The colon punctuation: Why doesn't 5:1 return [5, 4, 3, 2, 1]?

I got surprised by this:

``````julia> 1:5 # Expected
1:5

julia> 5:1 # Unexpected
5:4

julia> collect(1:5) # Expected
5-element Vector{Int64}:
1
2
3
4
5

julia> collect(5:1) # Doubly Unexpected
Int64[]

julia> collect(5:-1:1) # Ok, I *think* I get it...
5-element Vector{Int64}:
5
4
3
2
1

``````

The documentation says:

`a:b` colons (`:`) used as a binary infix operator construct a range from `a` to `b` (inclusive) with fixed step size `1`
Punctuation Â· The Julia Language

I expected the second output to be `5:1`, ie` [5, 4, 3, 2, 1]`. Ok, I can see that reading carefully the stepsize is 1, when I wanted -1. But why does it say` 5:4`? And why not have it automatically switch the sign if a > b?

By the same token, if `5:4` meant `5:-1:4` as you suggest, then it would be impossible to express empty ranges.

7 Likes

You pretty much answered your own question on why itâ€™s not equal to `[5,4,3,2,1]`: itâ€™s because thatâ€™s not its definition. Yes, thatâ€™s tautological. Steven has a good motivation for the current behavior, but a different choice could have been made.

Julia â€śnormalizesâ€ť empty ranges such that the end is one step less than the beginning, so thatâ€™s why itâ€™s `5:4` and not `5:1`. Why? IIRC it helps with some efficiencies for all ranges, even non-empty ones, if you can assume that.

Could docs be improved? Always!

8 Likes

Take a look at the function `range` and the rules become clearer. If you specify only `start` and `stop` the `step` is assumed to be `1`.

``````help?> range
search: range LinRange UnitRange StepRange StepRangeLen

range(start, stop, length)
range(start, stop; length, step)
range(start; length, stop, step)
range(;start, length, stop, step)

Construct a specialized array with evenly spaced elements and
optimized storage (an AbstractRange) from the arguments.
Mathematically a range is uniquely determined by any three of
start, step, stop and length. Valid invocations of range are:

â€˘  Call range with any three of start, step, stop,
length.

â€˘  Call range with two of start, stop, length. In this
case step will be assumed to be one. If both
arguments are Integers, a UnitRange will be
returned.

â€˘  Call range with one of stop or length. start and
step will be assumed to be one.
``````

With the colon syntax, you can specify `start:step:stop`.

1 Like

An empty range could be expressed as `5:1:4` (i.e. by explicitly specifying a positive step size)

I think there are a few different points to this topic.

• Definition/characteristics of ranges (start, length, stop, step)
• Why are empty ranges â€śnormalisedâ€ť
• What are default values for under-specified ranges, in particular using colon syntax.

In some contexts, I think it would be reasonable to expect differing defaults for when a range is under-specified. When start and stop are explicitly specified with literals (e.g. 3:7, 5:1), I donâ€™t think it is unreasonable to expect a default step size of +/-1 to make a non-empty range.

However, personally I do like the current default step size of +1 (allowing empty ranges), because it simplifies the following type of logic of iterating through 0 or more items:

``````# loop is skipped if n <= 0
for i in 1:n
...
end
``````

No, because then `m:step:n` would not be type stable. Julia uses a separate `UnitRange` type to represent `m:n` ranges (in which the step size is implicitly 1), in order to save 1/3 of the storage in this common case and to enable other optimizations.

``````julia> typeof(5:4)
UnitRange{Int64}

julia> typeof(5:1:4)
StepRange{Int64, Int64}
``````

Similarly, automatically converting `m:n` to `n:-1:m` when `n < m` would not be type stable.

2 Likes

Oh, good point.
Thanks for pointing this out!

Perhaps we should make `start:Val(step):stop` work.