Создание массивов из диапазонов в Julia без использования Collect () - PullRequest
2 голосов
/ 31 марта 2019

Я немного озадачен этим поведением Юлии при создании массивов из диапазонов.Я хочу знать основную механику следующего:

A = [1:10]

Результаты в 1-element Array{UnitRange{Int64},1}

, что не то, что я хотел.Код выше создает массив из UnitRange.В документации Julia рекомендуется использовать collect() для создания массивов из диапазонов следующим образом:

A = collect(1:10)

Результат в 10-element Array{Int64,1}.Идеальный.

Однако этот код также работает, если я добавляю точку с запятой после диапазона.

A = [1:10;]

Согласно документации Julia, ; - это короткая стрелка для vcat() или вертикали.конкатенации.Каково значение vcat при использовании его как A = [1:10;].Мало того, что это выглядит странно (с чем это связано?), Оно не имеет смысла для меня.

Мне бы хотелось получить четкое объяснение того, как диапазоны взаимодействуют с vcat.

1 Ответ

4 голосов
/ 31 марта 2019

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 в Юлии?

...