Hello,
I want to push a number of anonymous functions to an array. Each function is largely the same but in some cases some addends are scalars instead of variables. Say that there are three different cases, like:
case 1) f(x) = x[1]+x[3]+cos(x[1]-x[2])
case 2) f(x) = x[1]+x[3]+cos(a) where a is a scalar
case 3) f(x) = x[1]+x[3]+cos(x[1]-x[3])
(in reality the function is way bigger).
So, instead of writing something like this:
function_array = []
for case in cases
if case.type == 1
push!(functions_array, x->x[1]+x[3]+cos(x[1]-x[2]) )
elseif case.type ==2
etc.
I figure I’d use metaprogramming and do
function_array = []
for case in cases
if case.type == 1
cos_arg = :(x[1]-x[2])
elseif case.type == 2
cos_arg = :(a)
else
cos_arg = :(x[1]-x[3])
end
push!(function_array, x->x[1]+x[3]+cos(eval(Meta.parse(cos_arg))) )
end
But then I realized that eval(Meta.parse(...))
is in general a bad idea and eval
only does it in the global scope, which is not good in my case.
So I guess I should write a macro instead, but it’s something I never did and I am not sure on how to best approach this. I did read the metaprogramming section of the manual but I am still a bit confused, and could use some tips.
Hmm, maybe MWE is incomplete, but it looks like you do not need metaprogramming. Functions are first-class citizens in Julia, so you can use them as lego blocks to build arbitrarily complicated expressions.
function_array = []
cases = [(; type = 1), (; type = 2), (; type = 3)]
a = 1
for case in cases
f1 = if case.type == 1
x -> x[1] - x[2]
elseif case.type == 2
x -> a
else
x -> x[1] + x[3]
end
push!(function_array, x -> x[1] + x[3] + cos(f1(x)))
end
and sanity check
julia> map(i -> function_array[i]([1, 4, 7]), 1:3)
3-element Vector{Float64}:
7.010007503399555
8.54030230586814
7.854499966191386
1 Like
Thanks, that’s quite interesting and I did not know that!
However, I am afraid that I have over-simplified my problem, so in my actual case this solution does not seem to work. Let me see if I can explain my problem a bit more in detail. I am trying to write a function more like the following (again with the eval(Meta.parse(…)) approach):
function func_name(connected_branches::Array, case, function_array::Array, angle_from, angle_to)
ev_arg = eval ∘ Meta.parse ∘ string
if case == 1
cos_arg = :(deg2rad.([0, 120, -120])[c] - x[angle_to[br][d]]) # angle_from and angle_to are arrays of scalars
else
cos_arg = :(x[angle_from[c]] - x[angle_to[br][d]])
end
for c in 1:3
push!(function_array, x->sum( (x[1]+x[2]+cos(ev_arg(cos_arg)) for d in 1:3 if d!=c) for br in connected_branches) )
end
end
so, basically, if I try to apply the method you suggested directly, I get UndefVarError: br not defined
, because this is somehow “nested” in the variable index.
The inputs would look sort of like:
connected_branches = [1, 2]
case = 1
function_array = []
angle_from = [1, 2, 3]
angle_to = [[2, 4, 6], [1, 3, 5]]
I hope it is kind of clear and makes somewhat sense. Basically, angle_to
and angle_from
host the indexes of the variable array that I want to choose.
You can make c
, d
and br
arguments of internal functions
function func_name(connected_branches::Array, case, function_array::Array, angle_from, angle_to)
f1 = if case == 1
(x, c, d, br) -> deg2rad.([0, 120, -120])[c] - x[angle_to[br][d]] # angle_from and angle_to are arrays of scalars
else
(x, c, d, br) -> x[angle_from[c]] - x[angle_to[br][d]]
end
for c in 1:3
push!(function_array, x->sum( (x[1]+x[2]+cos(f1(x, c, d, br)) for d in 1:3 if d!=c) for br in connected_branches) )
end
end
1 Like
works perfectly, thank you!
1 Like