为许多类似功能实现多重分派的有效方法
Efficient way to implement multiple dispatch for many similar functions
我正在编写一些涉及各种函数形式的库的软件。我想利用 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
基本相同的函数。
就这样:
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 中进行矢量化非常简单,您应该尽可能考虑这种设计,
如果不需要安全或多重分派,则不应使用类型注释。例如。 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))
我正在编写一些涉及各种函数形式的库的软件。我想利用 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
基本相同的函数。
就这样:
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 中进行矢量化非常简单,您应该尽可能考虑这种设计,
如果不需要安全或多重分派,则不应使用类型注释。例如。 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))