Рекомендации по удержанию / циклическому просмотру коллекции объектов с одинаковым абстрактным родительским типом (Джулия) - PullRequest
4 голосов
/ 07 августа 2020

Это вопрос новичка, и я все еще думаю «в OOP», поэтому прошу прощения, если я пропустил ответ в руководстве или если ответ очевиден.

Предположим, у нас есть абстрактный тип,

abstract type My_Abstract_type end

и несколько конкретных типов структур, которые являются потомками этого типа:

mutable struct Concrete_struct1 <: My_Abstract_type end
mutable struct Concrete_struct2 <: My_Abstract_type end
...

Предположим, у нас есть большое количество объектов конкретных типов, и нам нужно store и l oop через эти объекты. В Python мы могли бы просто составить список объектов, а l oop - через список. Точно так же в C ++ мы могли бы создать массив указателей (типа My_Abstract_type) и l oop через него, полиморфно вызывая все необходимое.

Однако я не могу понять, как это сделать чисто в Юлии. Мы можем создать массив my_array::Array{My_Abstract_type,1}, а затем l oop через него:

for my_object in my_array
    do_something!(my_object)
end

, но, как здесь обсуждается https://docs.julialang.org/en/v1/manual/performance-tips/#man -performance-abstract-container-1 , это приводит к значительному снижению производительности (в моем случае это примерно в 25 раз медленнее).

Одна альтернатива - сделать что-то вроде:

my_array1::Array{Concrete_struct1,1}
my_array2::Array{Concrete_struct2,1}
my_array3::Array{Concrete_struct3,1}
...

, а затем

for my_object in my_array1
    do_something!(my_object)
end
for my_object in my_array2
    do_something!(my_object)
end
for my_object in my_array3
    do_something!(my_object)
end

Это дает нам желаемую производительность, но, очевидно, является ужасной практикой программной инженерии, особенно в случаях с большим количеством конкретных типов. Как мы можем хранить и l oop поверх этих объектов в Julia чисто и без ущерба для производительности ? Спасибо!

1 Ответ

3 голосов
/ 07 августа 2020

Если у вас не более четырех конкретных типов, просто используйте Union из них, как описано здесь в руководстве Julia. В таком случае компилятор сгенерирует вам эффективный код.

Если у вас очень много типов, вы можете использовать массив массивов:

a = [my_array1, my_array2, my_array3]

и теперь выполните

foreach(a) do x
    for my_object in x
        do_something!(my_object)
    end
end

Теперь a не будет иметь конкретного типа, но вызов анонимной функции должен разрешить специализацию кода. Это будет иметь некоторые накладные расходы (одна динамическая c отправка на элемент a), но при условии, что у вас гораздо больше элементов на массив, чем типов, это должно быть достаточно быстро.

Наконец, если вы хотите полностью избегайте динамической c отправки за счет принятия значительной стоимости компиляции, вы можете написать что-то вроде:

processing_fun() = nothing

function processing_fun(x, xs...)
    for my_object in x
        do_something!(my_object)
    end
    processing_fun(xs...)
end

, а затем позвонить:

processing_fun(a...)

(но это должно быть протестировано в вашем конкретном c случае, если это выгодно, поскольку оно включает рекурсию, которая также имеет свою стоимость)

...