Holding/Looping 通过具有相同抽象父类型的对象集合的最佳实践 (Julia)

Best-Practice for Holding/Looping Through a Collection of Objects with Same Abstract Parent Type (Julia)

这是一个初学者问题,我仍在“面向对象编程”中思考,所以如果我错过了手册中的答案或者答案很明显,我深表歉意。

假设我们有一个抽象类型,

abstract type My_Abstract_type end

以及作为该类型子级的几个具体结构类型:

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

假设我们有大量具体类型的对象,我们需要存储和循环这些对象。在 Python 中,我们可以只创建一个对象列表,然后循环遍历该列表。类似地,在 C++ 中,我们可以创建一个指针数组(My_Abstract_type 类型),然后遍历它,以多态方式调用所需的一切。

但是,我不知道如何在 Julia 中干净利落地做到这一点。我们可以创建一个数组 my_array::Array{My_Abstract_type,1} 然后遍历它:

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

这给了我们想要的性能,但显然是糟糕的软件工程实践,尤其是在有大量具体类型的情况下。我们如何在 Julia 中 干净地 存储和循环这些对象而不牺牲性能 ?谢谢!

如果您没有超过四种具体类型,则只需使用其中的 Union 个,如 Julia 手册中 here 所述。对于这种情况,编译器将为您生成有效的代码。

如果你有很多类型,那么你可以使用数组的数组:

a = [my_array1, my_array2, my_array3]

现在做

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

现在 a 本身没有具体类型,但对匿名函数的调用应该启用代码专业化。这将有一些开销(a 的每个元素一个动态调度),但假设每个数组的元素比类型多得多,它应该相当快。

最后,如果你想以接受大量编译成本为代价完全避免动态调度,你可以这样写:

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...)

(但如果它有益,则必须在您的特定情况下进行基准测试,因为它涉及递归,递归也有其成本)