编写一个将 AbstractVector 作为参数的函数

Writing a function that will take an AbstractVector as its parameter

来自 Performance Tips:

When working with parameterized types, including arrays, it is best to avoid parameterizing with abstract types where possible.

假设您正在编写一个需要 Vector 的函数,并且每次使用该函数时,Vector 可以包含不同的类型。

function selection_sort!(unsorted_vect::AbstractVector)
    # Code to sort.
end 

names = String["Sarah","Kathy","Amber"]
unsorted_fibonacci = Int32[8, 1, 34, 21, 3, 5, 0, 13, 2, 1]
    
selection_sort!(names)
selection_sort!(unsorted_fibonacci)

第一次使用selection_sort!()时,Vector包含String,第二次Int32.

该函数知道它正在获取一个 AbstractVector,但它不知道元素的类型。

  1. 性能会不会低效?会不会和写selection_sort!(unsorted_vect::AbstractVector{Real})一样?

同节还说:

If you cannot avoid containers with abstract value types, it is sometimes better to parametrize with Any to avoid runtime type checking. E.g. IdDict{Any, Any} performs better than IdDict{Type, Vector}

  1. 这样写函数会不会更好?
function selection_sort!(unsorted_vect::AbstractVector{Any})

如果是这样,为什么 sorting algorithms 只使用 AbstractVector

function sort!(v::AbstractVector, # left out other parameters)

您需要的是:

function selection_sort!(unsorted_vect::AbstractVector{T}) where T
    # Code to sort.
end 

这样你就有了容器的抽象类型(因此任何容器都会被接受)。 然而,这些容器可以是 non-abstract - 因为它们的元素可以有一个具体类型 - T。正如 Bogumil 所指出的那样 - 如果在正文代码中您不需要 T 类型,您可以改为 function selection_sort!(unsorted_vect::AbstractVector)

但是,这只是参数类型的限制。 Julia 会很高兴拥有 function selection_sort!(unsorted_vect) 并且代码会同样高效。

真正重要的是参数的类型。考虑以下 3 个变量:

a = Vector{Float64}(rand(10))
b = Vector{Union{Float64,Int}}(rand(10))
c = Vector{Any}(rand(10))

a是类型容器,b是small-union类型容器,c是抽象容器。让我们看看性能发生了什么:

julia> @btime minimum($a);
  18.530 ns (0 allocations: 0 bytes)

julia> @btime minimum($b);
  28.600 ns (0 allocations: 0 bytes)

julia> @btime minimum($c);
  241.071 ns (9 allocations: 144 bytes)

变量 c 需要对值进行拆箱(由于元素类型未知),因此性能下降了一个数量级。

总而言之,参数类型才是真正重要的。