I am writing some software that involves a library of various functional forms of a quantity. I want to leverage Julia's multiple dispatch, but want to know if there's a more efficient way to implement this procedure.
Consider, for example, a library that contains the following two functions
function firstfunction(x::Float64)
return 2*x
end
function secondfunction(x::Float64)
return x^2
end
I would also like to implement multiple dispatch methods that can apply these functional forms to an vector of values, or an array of vectors (matrix). I could do this as follows
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
But since all three versions of the function use the same kernel, and the actions of the various dispatches are the same across all functional forms, I'd like to know if there's a more efficient way to write this such that defining a new function for the library (e.g thirdfunction
) only involves explicitly writing the kernel function, rather than having to type out 2*n
essentially identical functions for n
functional forms in the library.
Just do:
function thirdfunction(x::Union{Number, Array{<:Number}})
return x.^0.5
end
This is the beauty of multiple-dispatch in 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
Note that however in your case it might make sense to have only a single representation of a function and let the user decide to vectorize it using the dot operator (.
).
function fourthfunction(x::Real)
min(x, 5)
end
And now the user just needs to add a dot when needed:
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
Since vectorizing in Julia is so easy you should consider this design whenever possible,