range
s - это «ленивые» векторы, которые никогда не выделяются.Это, наверное, один из самых полезных итераторов.
julia> AbstractRange <: AbstractVector
true
julia> @allocated [1,2,3,4,5,6,7,8,9,10]
160
julia> @allocated 1:10
0
оператор диапазона :
предназначен для создания диапазонов:
julia> 1:10 |> dump
UnitRange{Int64}
start: Int64 1
stop: Int64 10
вы уже знали, как преобразовать диапазоны в векторы, используя collect
, но если бы вы могли нырнутьнемного глубже в коде, вы найдете collect
на самом деле вызовы vcat
под капотом:
julia> @less collect(1:10)
collect(r::AbstractRange) = vcat(r)
и вот как vcat
справляется с AbstractRange
вводом:
@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
реализация очень проста, просто проходя по циклу ввода (примечание rs
- это ввод vararg), и объединяя входные диапазоны один за другим в один вектор.очевидно, он работает, даже когда есть только один входной диапазон, как в случае [1:10;]
.
существует еще один способ создания векторов из диапазонов: прямой вызов Vector
конструктора Vector(1:10)
.а что происходит под капотом?простой вызов @less Vector(1:10)
не приведет к непосредственному переходу к исходной реализации, и вот тут появляется модный отладчик:
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))
, как вы можете видеть, Vector
также вызывает vcat
.
Я думаю, что этот пример уже дал вам некоторые идеи о том, как самостоятельно найти ответ в Julia REPL с помощью этих замечательных удобных встроенных инструментов отражения.Существуют и другие полезные инструменты, такие как @code_lowered
, @code_typed
, @macroexpand
и т. д., которые могут помочь вам выяснить такие вопросы, как «что делает это выражение?», например,
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«пониженный» код говорит нам, что Джулия сначала создает диапазон %1 = 1:10
, а затем вызывает Base.vcat(%1)
, что в точности соответствует документации.
X-ref: В чем разница между @code_native, @code_typed и @code_llvm в Юлии?