Юлия: массив массивов с разными типами - PullRequest
6 голосов
/ 26 февраля 2020

Джулия новичок здесь, переходя от python.

Итак, я хочу построить то, что в Python я бы назвал списком, составленным из списков, составленных из списков. В моем случае это длинный список из 1000 элементов, элемент которого представляет собой список из 3 списков.

До сих пор я делал это следующим образом:

BIG_LIST = collect(Array{Int64,1}[[],[],[]] for i in 1:1000)

Это служило моей цели, когда все три самых внутренних списка были составлены из целых чисел. Теперь мне нужно, чтобы 2 из них были целыми числами, а третье число с плавающей точкой. Это возможно? Как мне это сделать?

Если бы вы могли объяснить, как правильно инициализировать эти объекты, это было бы здорово. Я знаю, что коллекционирование здесь не лучший выбор.

Обратите внимание, что длина трех внутренних списков одинакова среди трех, но может изменяться в процессе.

Ответы [ 2 ]

8 голосов
/ 26 февраля 2020

Во-первых, если вы знаете, что в промежуточных списках всегда есть 3 элемента, вам, вероятно, будет лучше использовать Tuple types для них. И кортежи могут независимо указывать типы своих элементов. Таким образом, что-то вроде этого может соответствовать вашим целям:

julia> l = [(Int64[], Int64[], Float64[]) for _ in 1:10]
10-element Array{Tuple{Array{Int64,1},Array{Int64,1},Array{Float64,1}},1}:
 ([], [], [])
 ([], [], [])
 ([], [], [])
 ([], [], [])
 ([], [], [])
 ([], [], [])
 ([], [], [])
 ([], [], [])
 ([], [], [])
 ([], [], [])

julia> push!(l[1][3], 5)
1-element Array{Float64,1}:
 5.0

julia> l
10-element Array{Tuple{Array{Int64,1},Array{Int64,1},Array{Float64,1}},1}:
 ([], [], [5.0])
 ([], [], [])   
 ([], [], [])   
 ([], [], [])   
 ([], [], [])   
 ([], [], [])   
 ([], [], [])   
 ([], [], [])   
 ([], [], [])   
 ([], [], [])   

Несколько замечаний, которые могут вас заинтересовать:

  • Пустые, но типизированные списки могут быть построенным с использованием T[], где T - это тип элемента.

  • collect(f(i) for i in 1:n) по существу эквивалентно простому пониманию (как вы привыкли в python) : [f(i) for i in 1:n]. Обратите внимание, что поскольку переменная i здесь не играет никакой роли, вы можете заменить ее заполнителем _, чтобы читателю сразу стало понятно, что вы по сути создаете коллекцию похожих объектов (но не идентичных, в том смысле, что они не разделяют одну и ту же базовую память; изменение одной не повлияет на другие).

  • Я не знаю лучшего способа инициализации такой коллекции, и я бы не стал не думаю, что использование collect (или понимание) здесь плохая идея. Для коллекций идентичных объектов fill предоставляет полезный ярлык, но он не будет применяться здесь, потому что все подсписки будут связаны.

Теперь, если все внутренние подсписки имеют одинаковую длину, вы можете переключиться на немного другую структуру данных: вектор векторов кортежей:

julia> l2 = [Tuple{Int64,Int64,Float64}[] for _ in 1:10]
10-element Array{Array{Tuple{Int64,Int64,Float64},1},1}:
 []
 []
 []
 []
 []
 []
 []
 []
 []
 []

julia> push!(l2[2], (1,2,pi))
1-element Array{Tuple{Int64,Int64,Float64},1}:
 (1, 2, 3.141592653589793)

julia> l2
10-element Array{Array{Tuple{Int64,Int64,Float64},1},1}:
 []                         
 [(1, 2, 3.141592653589793)]
 []                         
 []                         
 []                         
 []                         
 []                         
 []                         
 []                         
 []                         
5 голосов
/ 27 февраля 2020

Франсуа дал вам отличный ответ. Я просто хотел поднять еще одну возможность. Похоже, ваши данные имеют довольно сложную, но конкретную c структуру. Например, тот факт, что ваш внешний список содержит 1000 элементов, а ваш внутренний список всегда имеет 3 списка ...

Иногда в этих ситуациях может быть более интуитивно просто создать собственный тип (ы), и написать пару функций доступа. Таким образом, вы не будете в конечном итоге делать такие вещи, как mylist[3][2][6] и забывать, какой индекс относится к какому измерению ваших данных. Например:

struct MyInnerType
    field1::Vector{Int}
    field2::Vector{Int}
    field3::Vector{Float64}
end
struct MyOuterType
    x::Vector{MyInnerType}
    function MyOuterType(x::Vector{MyInnerType})
        length(x) != 1000 && error("This vector should always have length of 1000")
        new(x)
    end
end

Я предполагаю здесь, но, возможно, такие функции доступа были бы полезны для, например, field3:

get_field3(y::MyInnerType, i::Int)::Float64 = y.field3[i]
get_field3(z::MyOuterType, iouter::Int, iinner::Int)::Float64 = get_field3(z.x[iouter], iinner)

Помните, что нет снижения производительности использовать ваши собственные типы в Julia.

Еще одна вещь, я включил всю информацию о типах в свои функции выше для ясности, но на самом деле это не обязательно для получения максимальной производительности.

...