I am a bit puzzled by this behavior in Julia when creating arrays from ranges. I want to know the underlying mechanics of the following.
A = [1:10]
Results in 1-element Array{UnitRange{Int64},1}
which is not what I wanted. Above code creates an Array of UnitRange
. Julia documentation recommends using collect()
to create arrays from ranges as follows:
A = collect(1:10)
Results in 10-element Array{Int64,1}
. Perfect.
However, this code also works if I add a semicolon after the range.
A = [1:10;]
According to Julia documentation, ;
is a short hand for vcat()
or vertical concatenation. What is the significance of vcat
when using it as A = [1:10;]
. Not only does it look weird (what is it vcat'ing it with?), it doesn't make sense to me.
I'd love a clear explanation about how ranges interact with vcat.
An Array in Julia can be created with the use of a pre-defined keyword Array() or by simply writing array elements within square brackets([]). There are different ways of creating different types of arrays.
Empty Arrays To initialize an empty array, it is perfectly valid to use n = 0 in the above expressions.
Julia provides a very simple notation to create matrices. A matrix can be created using the following notation: A = [1 2 3; 4 5 6]. Spaces separate entries in a row and semicolons separate rows. We can also get the size of a matrix using size(A).
range
s are "lazy" vectors that never allocate. It's probably one of the most useful iterators.
julia> AbstractRange <: AbstractVector
true
julia> @allocated [1,2,3,4,5,6,7,8,9,10]
160
julia> @allocated 1:10
0
the range operator :
is for creating ranges:
julia> 1:10 |> dump
UnitRange{Int64}
start: Int64 1
stop: Int64 10
you've already known how to convert ranges to vectors using collect
, but if you could dive a little bit deeper into the code, you would find collect
actually calls vcat
under the hood:
julia> @less collect(1:10)
collect(r::AbstractRange) = vcat(r)
and this is how vcat
deal with AbstractRange
input:
@less vcat(1:10)
function vcat(rs::AbstractRange{T}...) where T
n::Int = 0
for ra in rs
n += length(ra)
end
a = Vector{T}(undef, n)
i = 1
for ra in rs, x in ra
@inbounds a[i] = x
i += 1
end
return a
end
the implementation is very simple, just looping through the input (note rs
is a vararg input), and concating input ranges one-by-one into a single vector. obviously, it works even when there is only one input range, which is the case of [1:10;]
.
there is another way to create vectors from ranges: directly calling the Vector
constructor Vector(1:10)
. but what happens under the hood? simply calling @less Vector(1:10)
won't directly jump to the original implementation, and this is where the fancy debugger comes in:
julia> using Debugger
julia> @enter Vector(1:10)
In Type(x) at boot.jl:424
>424 (::Type{Array{T,N} where T})(x::AbstractArray{S,N}) where {S,N} = Array{S,N}(x)
About to run: (Core.apply_type)(Array, Int64, 1)
1|debug> s
In Type(x) at boot.jl:424
>424 (::Type{Array{T,N} where T})(x::AbstractArray{S,N}) where {S,N} = Array{S,N}(x)
About to run: (Array{Int64,1})(1:10)
1|debug> s
[ Info: tracking Base
In Type(r) at range.jl:943
>943 Array{T,1}(r::AbstractRange{T}) where {T} = vcat(r)
About to run: (vcat)(1:10)
1|debug> s
In vcat(rs) at range.jl:930
>930 n::Int = 0
931 for ra in rs
932 n += length(ra)
933 end
934 a = Vector{T}(undef, n)
About to run: Core.NewvarNode(:(_5))
as you can see, Vector
also calls vcat
.
I think this example has already given you some ideas about how to interactively find the answer in Julia REPL by yourself with these great handy built-in reflection tools. there are other useful tools like @code_lowered
, @code_typed
, @macroexpand
etc. that could help you to figure out questions like "what does this expression do?", for example,
julia> f() = [1:10;]
f (generic function with 1 method)
julia> @code_lowered f()
CodeInfo(
1 ─ %1 = 1:10
│ %2 = (Base.vcat)(%1)
└── return %2
)
the "lowered" code tells us that Julia firstly create a range %1 = 1:10
and then call Base.vcat(%1)
, which is exactly what the documentation said.
X-ref: What is the difference between @code_native, @code_typed and @code_llvm in Julia?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With