First post here, thanks for reading!
Problem: I have a Vector{String}
- call it A
- where each element is a part of an equation, e.g. the first element of A
is "x[1] - (0.8*x[1])"
. I would like to write a macro that takes as arguments i) a String
- call it fn_name
- with the name of a function, ii) the vector A
, and returns a function named fn_name
which looks like
function fn_name(f, x)
f[1] = x[1] - (0.8*x[1])
f[2] = (exp(x[4]) - 0.8*exp(x[3]))^(-1.1) - (0.99*(exp(x[4]) - 0.8*exp(x[4]))^(-1.1)*(1.0 - 0.025 + 0.30*exp(x[1])*exp(x[2])^(0.30 - 1.0)))
f[3] = exp(x[2]) - ((1.0 - 0.025)*exp(x[2]) + exp(x[1])*exp(x[2])^0.30 - exp(x[4]))
f[4] = x[3] - (x[4])
end
where each rhs is one element of
A = ["x[1] - (0.8*x[1])", "(exp(x[4]) - 0.8*exp(x[3]))^(-1.1) - (0.99*(exp(x[4]) - 0.8*exp(x[4]))^(-1.1)*(1.0 - 0.025 + 0.30*exp(x[1])*exp(x[2])^(0.30 - 1.0)))", "exp(x[2]) - ((1.0 - 0.025)*exp(x[2]) + exp(x[1])*exp(x[2])^0.30 - exp(x[4]))", "x[3] - (x[4])"]
What I tried: my best attempt at solving the problem is the following
macro make_fn(fn_name, A)
esc(quote
function $(Symbol(fn_name))(f, x)
for i = 1:length($(A))
f[$i] = Meta.parse($(A)[$i])
end
end
end)
end
which however doesn't work: when I run @make_fn("my_name", A)
I get the error LoadError: UndefVarError: i not defined
.
I find it quite hard to wrap my head around Julia metaprogramming, and while I'd be very happy to avoid using it, I think for this problem it's unavoidable.
Can you please help me understand where my mistake is?
Thanks
Macros have a dedicated character in Julia's syntax: the @ (at-sign), followed by the unique name declared in a macro NAME ... end block.
Functions in Julia can be combined by composing or piping (chaining) them together. Function composition is when you combine functions together and apply the resulting composition to arguments. You use the function composition operator ( ∘ ) to compose the functions, so (f ∘ g)(args...) is the same as f(g(args...)) .
Macros change existing source code or generate entirely new code. They are not some kind of more powerful function that unlocks secret abilities of Julia, they are just a way to automatically write code that you could have written out by hand anyway.
Metaprogramming may be defined as the programming in which we write Julia code to process and modify Julia code. With the help of Julia metaprogramming tools, one can write Julia programming code that modifies other parts of the source code file. These tools can even control when the modified code runs.
Macros in this case are not only avoidable, but even inapplicable, unless A
is literal known at compile time.
I can provide a solution using eval
and some closures:
julia> function make_fn2(A)
Af = [@eval(x -> $(Meta.parse(expr))) for expr in A]
function (f, x)
for i in eachindex(A, f)
f[i] = Af[i](x)
end
return f
end
end
make_fn2 (generic function with 1 method)
julia> fn_name = make_fn2(A)
#46 (generic function with 1 method)
julia> fn_name(zeros(4), [1,2,3,4])
4-element Array{Float64,1}:
0.19999999999999996
-0.06594092302655707
49.82984401122239
-1.0
with the restrictions that
eval
will evaluate the expressions in a global scope of the module where this is defined (so it is potentially a different scope that a scope of the calling function), andBut I'd really recommend thinking about a better input format than strings.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With