Help understand the role of semicolon in turning a UnitRange to a Vector

I understand the difference between the UnitRange and Array/Vector types. I sort of get the syntax for stacking two ranges and converting them into a vector using ; and [] as in

julia> a = [1:3;4:5]
5-element Vector{Int64}:
 1
 2
 3
 4
 5

But I think I need some hint on how to remember/understand/interpret the usage of semicolon for converting just a single unit range into a vector as in:

julia> a = [1:3;]
3-element Vector{Int64}:
 1
 2
 3

The documentation on puncuation gives this description for semicolon:

semicolons separate statements, begin a list of keyword arguments in function declarations or calls, or are used to separate array literals for vertical concatenation

and I am just not able to fit this usage to the above described conversion of a range into a vector.

it’s this.

julia> vcat(1:3)
3-element Vector{Int64}:
 1
 2
 3

julia> [1:3;]
3-element Vector{Int64}:
 1
 2
 3

julia> vcat(1:3, 4:6)
6-element Vector{Int64}:
 1
 2
 3
 4
 5
 6

julia> [1:3;4:6]
6-element Vector{Int64}:
 1
 2
 3
 4
 5
 6
4 Likes

I suspected that it should be interpreted like this. Simply a (vertical) concatenation of something with nothing, right?

I don’t see how “with nothing” is a useful concept here. You can concatenate any number of abstract vectors into a vector, any number includes 1 and 0.

3 Likes

Perhaps I am just too biased to see the semicolon as a separator of two things. That is why I somehow easily accepted the syntax [a;b] and struggled mentally with [a;]. But most probably there is not much more to dig here. I will just remember it. Thanks.

2 Likes

Yes instead of separators I think it’s best to think of [a, b, ...], [a; b; ...], [a b ...] and [a b; c d; ...] as fancy syntax for four different functions.

Four lines above in your linked documentation page, there’s a description specifically for the case of array literals:

[;] vertical concatenation (calling vcat or hvcat)
[ ] with space-separated expressions, horizontal concatenation (calling hcat or hvcat )

In you example:

  • [1:3] is a special case of [a, b, ...] with only one argument a=1:3. This is fancy syntax for the Base.vect function:

    julia> Meta.@lower [1:3]
    :($(Expr(:thunk, CodeInfo(
        @ none within `top-level scope'
    1 ─ %1 = 1:3
    │   %2 = Base.vect(%1)
    └──      return %2
    ))))
    

    So [1:3] is simply a vector with one element: 1:3.

  • [1:3;] is a special case of [a; b; ...] with one argument 1:3. That’s fancy syntax for vcat:

    julia> Meta.@lower [1:3;]
    :($(Expr(:thunk, CodeInfo(
        @ none within `top-level scope'
    1 ─ %1 = 1:3
    │   %2 = Base.vcat(%1)
    └──      return %2
    ))))
    

    The vcat function concatenates arrays vertically. Here “array” means any subtype of AbstractArray (other types are treated as single-element arrays). And a range such as 1:3 is an AbstractArray:

    julia> supertypes(typeof(1:3))
    (UnitRange{Int64}, AbstractUnitRange{Int64}, OrdinalRange{Int64, Int64}, 
    AbstractRange{Int64}, AbstractVector{Int64}, Any)
    
    julia> AbstractVector
    AbstractVector{T} where T (alias for AbstractArray{T, 1} where T)
    

    So that’s why the 1:3 range gets “flattened” into three values in the resulting vector.

(As for the remaining cases: [a b ...] is fancy syntax for hcat and [a b ...; c d ...; ...] is fancy syntax for hvcat. Both of these also treat their arguments as iterators.)

4 Likes

No idea if this is a correct mental model but, in case it helps, may see it as concatenation with empty []:

a=1:3
julia> [a;] == [a;eltype(a)[]]
true
1 Like