I am making a type that holds another type as one of its fields, like so
struct A
a::Float64
end
f(a::A, x::Real) = A.a * x
# plus whole other bunch of functions with similar arguments
g(a::A, x::Real) = ...
h(a::A, x::Real) = ...
struct B
a1::A
a2::A
end
Now I want the functions that operate on B to use the A objects inside, depending on the value of x, like so
function f(b::B, x::Real)
if x >= 0.0
return f(b.a1, x)
else
return f(b.a2, x)
end
end
However, as listed above there are a bunch of functions that operate on A with x as an input, and it seems cumbersome to copy such a similar function signature many times. So is there a way to pass through the function as well? Something like this (obviously this doesn’t work):
function (function_name)(b::B, x::Real) where {function_name}
if x >= 0.0
return function_name(b.a1, x)
else
return function_name(b.a2, x)
end
end
This example above I would only have to write once, and it would work for f, g, h, and any such function that is defined on A. Is something like this possible in Julia?
I am not sure if I completely understood you problem, but you can pass functions as any other parameter:
julia> f(a,b) = min(a,b)
f (generic function with 1 method)
julia> g(a,b) = max(a,b)
g (generic function with 1 method)
julia> h(x,y,f) = f(x,y)
h (generic function with 1 method)
julia> h(1,2,f)
1
julia> h(1,2,g)
2
It sounds like your type B is a container for type A objects, and applying a function f to B should apply f to each A element. Possibly the cleanest way to do so is to use broadcasting, so you can write f.(b,x).
Thanks for the replies, I’ll try to clarify a bit.
So I’m trying to find out how to avoid having to write the following code for every function that acts on A to also work on B (where again, depending on the value of x, a different A object is selected for the function evaluation):
function f(b::B, x::Real)
if x >= 0.0
return f(b.a1, x)
else
return f(b.a2, x)
end
end
function g(b::B, x::Real)
if x >= 0.0
return g(b.a1, x)
else
return g(b.a2, x)
end
end
function h(b::B, x::Real)
if x >= 0.0
return h(b.a1, x)
else
return h(b.a2, x)
end
end
This code is all so similar, that I figured there would be a way to only write this logic once and have it be applied to every function that acts on A (something along the lines of what I wrote in the last code block in my original question).
I think this is a bit different than broadcasting, as I don’t want f (and other functions) to simply work on each A object inside of B but rather use the value of x to select which A object to use.
@lmiq: thanks for your reply. I should note that the functions f, g, h, etc are already used throughout the code base, and your suggestion would require changing a bunch of code (wherever f, g, etc. are called). (That’s my bad, I should’ve made that clear)
Just to make sure I’m interpreting your answer correctly. You’re suggesting this?
for op = (:h, :g, :h)
eval(
quote
function $op(b::B, x::Real)
if x >= 0.0
return $op(b.a1, x)
else
return $op(b.a2, x)
end
end
end
)
end
Are there any problems with that approach in terms of dispatch, type-stability or performance? (or does it simply just generate the code and that’s it?)
Yup, exactly. The only downside is that it can make your code harder to read, especially since your text editor or IDE may not be able to figure out where the functions are defined.