I have a function f that creates and filsl a vector of n components with the result of expensive functions gi. I am interested in having f depend on n. I have tried the following, but I’m wondering if there is a better, perhaps more elegant way:
function f(n)
a = Vector{Float64}(undef, n)
(n >= 1) && (a[1] = g1(x, y, z))
(n >= 2) && (a[2] = g2(x, y, z))
(n >= 3) && (a[3] = g3(x, y, z))
(n >= 4) && (a[4] = g4(x, y, z))
(n >= 5) && (a[5] = g5(x, y, z))
return sum(a)
end
The problem is that gi are hard to parameterize with respect to i. I guess I can create another function, but then I would just write that hard coding of gi to that auxiliary function, instead of inside f.
I’m not worried about performance here. This is just a mwe. Those x,y,z are just my implicit attempt at saying “don’t worry about that”.
I take your point about allocating a vector. Perhaps this is better:
function f(n)
a = 0.0
(n >= 1) && (a += g1(x, y, z))
(n >= 2) && (a += g2(x, y, z))
(n >= 3) && (a += g3(x, y, z))
(n >= 4) && (a += g4(x, y, z))
(n >= 5) && (a += g5(x, y, z))
return a
end
I think it’s good to get into this way of thinking as a matter of habit. This is a typical performance trap, and it’s good to just always keep these things at the back of your mind. The same goes for the unnecessary vector.
using ComponentArrays
using Parameters
args = ComponentArray(x1 = 1, x2 = 2, y2 = 2, x3 = 3, y3 = 3, z3 = 3)
function g1(a1, args)
@unpack x1 = args
@assert x1 == a1
sleep(1)
return x1
end
function g2(a2, b2, args)
@unpack x2, y2 = args
@assert x2 == a2 && y2 == b2
sleep(2)
return x2
end
function g3(a3, b3, c3, args)
@unpack x3, y3, z3 = args
@assert x3 == a3 && y3 == b3 && z3 == c3
sleep(3)
return x3
end
function f(n, args)
@unpack x1, x2, y2, x3, y3, z3 = args
a = zero(eltype(args))
(n >= 1) && (a += g1(1, args))
(n >= 2) && (a += g2(2, 2, args))
(n >= 3) && (a += g3(3, 3, 3, args))
return a
end
That should run without problem. What I am looking for is whether there exists a better way of doing what f is doing. That is, only evaluating the functions gi when they are necessary.
A bit in a hurry here, but I would wrap both the functions and the arguments in two different containers, either tuples or vectors, and then do something like this:
function f(fun, args, n=length(fun))
a = zero(something)
for i in 1:n
a += fun[i](args[i]...)
end
return a
end