Conditional evaluation of lines

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

Have an array of functions, or pass i as a parameter to g? That way you can write a loop.

There’s not much point in allocating an array a just to sum it. Why not just add up the results directly?

Also, where are x,y,z coming from? Hopefully not globals?

Combining the above suggestions, you could have e.g. f(n, x,y,z) = sum(i -> g(i,x,y,z), 1:n).

2 Likes

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

The suggestion of using an array of functions
is nice and general:

gs = (g1, g2, g3, g4, g5)
sum(i -> gs[i](x,y,z), 1:n)
3 Likes

suppose each argument that goes inside the gi is different, can I form an array like your gs that doesn’t evaluate when it is assigned?

What does this mean? Are you sure that your MWE captures the essential parts of the problem?

Not sure I understand, but do you mean something like this:

gs = (()->g1(x, y, z), ()->g2(a, b),...) 

?

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.

BTW, it’s not an actual MWE unless it runs.

3 Likes

Let me try again.

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
2 Likes