Golfing question

Hi, I often have the following structure in my code: a function Bar calls a function Foo with multiple outputs and then uses all its outputs to return something (MWE below). Is there a performant way of golfing such functions into one-line functions? Performant here means: no loss in speed (or even an improvement), no decrease in type stability etc.

Very simple example (where it is of course not the purpose to unfold the definition of Foo in Bar, that defeats the purpose):

function Foo(x::Real) 
    return x, x^2, x^3
end

function Bar(x::Real)
    x, y, z = Foo(x)
    return Foo2(x, z, y)
end

here, Foo2 is some complicated function.

I know how to golf Foo:

Foo(x::Real) = (x, x^2, x^3)

But how do I golf Bar to something like

Bar(x::Real) = Foo2(Foo(x)[1], Foo(x)[3], Foo(x)[2])

without recomputing Foo(x) three times? (Note the change of order in the arguments).

You can use splatting: Bar(x::Real) = Foo2(Foo(x)...)

3 Likes

Thank you for your response! I’m aware of splatting, but I thought there were performance concerns with this?

Also, it wouldn’t work if I needed something like Foo2(Foo(x)[1], Foo(x)[3], Foo(x)[2]); I’ll change the original post to reflect that!

You can do Foo2(getindex.(Foo(x), (1, 3, 2))...) and that should not lead to any overhead due to the literal tuple const folding away. If Foo is not scalar-broadcastable you can wrap it in Ref(...)

2 Likes

The performance concerns with splatting is when the compiler does not know the length and types of the elements. Splatting an NTuple{3, T} the compiler knows exactly which method to call.

Splatting a Vector{T}, on the other hand, has performance implications, since its length is not statically known.

7 Likes

Thanks for clarifying! Does this also apply if each output element has a different type?

Yes, as long as the types are known.

2 Likes

If you’re willing to use the full function syntax you can also simply

function Bar(x::Real)
    x1, x2, x3 = Foo(x)
    return Foo2(x1, x3, x2)
end

But for code golfing using the long syntax basically defeats the purpose…

1 Like

Ah, “golfing” as in code-minimization…

Foo(x) = x.^(1:3)
f(x, y, z) = (x, z, y)
Bar(x) = Foo2(f(x)...)