【问题标题】:Efficient way to implement multiple dispatch for many similar functions为许多类似功能实现多次调度的有效方法
【发布时间】:2021-03-13 14:57:29
【问题描述】:

我正在编写一些软件,其中涉及一个数量的各种函数形式的库。我想利用 Julia 的多次调度,但想知道是否有更有效的方法来实现这个过程。

例如,考虑一个包含以下两个函数的库

function firstfunction(x::Float64)
    return 2*x
end

function secondfunction(x::Float64)
    return x^2
end

我还想实现多个调度方法,可以将这些函数形式应用于值向量或向量数组(矩阵)。我可以这样做

function firstfunction(x::Float64)
    return 2*x
end

function firstfunction(xs::Vector{Float64})
    f = similar(xs)
    for i = 1:size(xs, 1)
        f[i] = 2*xs[i]
    end
    return f
end

function firstfunction(xss::Matrix{Float64})
    f = similar(xss)
    for i = 1:size(xss, 1)
        for j = 1:size(xss, 2)
            f[i, j] = 2*xss[i, j]
    end
    return f
end

function secondfunction(x::Float64)
    return x^2
end

function secondfunction(xs::Vector{Float64})
    f = similar(xs)
    for i = 1:size(xs, 1)
        f[i] = xs[i]^2
    end
    return f
end

function secondfunction(xss::Matrix{Float64})
    f = similar(xss)
    for i = 1:size(xss, 1)
        for j = 1:size(xss, 2)
            f[i, j] = xss[i, j]^2
    end
    return f
end

但是由于函数的所有三个版本都使用相同的内核,并且各种调度的操作在所有函数形式中都是相同的,我想知道是否有更有效的方法来编写它,以便定义一个库的新函数(例如thirdfunction)只涉及显式编写内核函数,而不必为库中的n 函数形式键入基本相同的2*n 函数。

【问题讨论】:

    标签: julia multiple-dispatch


    【解决方案1】:

    只要做:

    function thirdfunction(x::Union{Number, Array{<:Number}})
        return x.^0.5
    end
    

    这就是 Julia 中多重调度的美妙之处:

    julia> thirdfunction(4)
    2.0
    
    julia> thirdfunction([4,9])
    2-element Array{Float64,1}:
     2.0
     3.0
    
    julia> thirdfunction([4 9; 16 25])
    2×2 Array{Float64,2}:
     2.0  3.0
     4.0  5.0
    

    请注意,但是在您的情况下,仅使用一个函数表示并让用户决定使用点运算符 (.) 对其进行矢量化可能是有意义的。

    function fourthfunction(x::Real)
        min(x, 5)
    end
    

    现在用户只需要在需要时添加一个点:

    julia> fourthfunction(4)
    4
    
    julia> fourthfunction.([4,9])
    2-element Array{Int64,1}:
     4
     5
    
    julia> fourthfunction.([4 9; 16 25])
    2×2 Array{Int64,2}:
     4  5
     5  5
    

    由于 Julia 中的矢量化非常简单,因此您应该尽可能考虑这种设计,

    【讨论】:

      【解决方案2】:

      如果安全或多次分派不需要类型注释,则不应使用它们。例如。 firstfunction 不太可能只适用于 Float64,可能它应该适用于所有数字,因此写

      function firstfunction(x::Real) # or just firstfunction(x)
          return 2*x
      end
      

      定义更通用的函数不会降低性能。

      回到主题: 对于将函数应用于矩阵/向量等,最简单的方法是使用广播:

      A = rand(10, 10) # 10x10 matrix
      B = firstfunction.(A) # apply element-wise
      

      如果你想自己定义如何应用另一个函数,你可以使用函数作为输入参数,例如:

      thirdfunction(f, x) = f(f(x))
      

      【讨论】:

      • 在我给出的示例中,将其定义为仅适用于 Float64 会很奇怪,但这些只是简单的虚拟函数,在我的特定用例中,Float64 需要 Float32 以确保准确性,并且是一个复数输入将是无效的,所以我认为这些函数应该只适用于 Float64
      • Real 已经不包括复数,但包括例如Float128 或 BigFloat,将来可能需要。如果您确定始终将它与 Float64 一起使用,并且想要此显式声明进行类型检查,那么使用此类型注释当然没问题。
      猜你喜欢
      • 1970-01-01
      • 2019-07-31
      • 2011-12-31
      • 1970-01-01
      • 1970-01-01
      • 2015-12-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多