I am new to metaprograming in Julia. The case is I have many vectors to initialize in a constructor. I came up with this code
function MyStruct(n::Int)
vecs = [:x, :y, :z, :p]
for v in vecs
@eval $v = Vector{Float64}(undef, $n)
end
return @eval MyStruct($(vecs...))
end
Then I found that @eval is always evaluated in the module’s global scope. It is not a good idea to use @eval within a function. It can be performance killer. Also, @eval does not have scope to the local variable n, so I have to do interpolation, adding $ to every local variable in my function.
Any suggestions for the correct way of replacing repeated vector initialization within a constructor function?
Thank you! You are right. In this constructor case, it indeed doesn’t require macros. And seems like we don’t even need to define vecs if not for readability. A follow up question, in a another case, I want to assign vectors to a list of fields in a struct
mutable struct NewStruct
x::Vector{Float64}
y::Vector{Float64}
z::Vector{Float64}
p::Vector{Float64}
NewStruct() = new()
end
function init(a::NewStruct)
vecs = [:x, :y, :z, :p]
for v in vecs
@eval a.$v = zeros(10)
end
end
You don’t need a macro or eval() in this case. You can just use the setproperty! function which already does what you want:
julia> function init(a::NewStruct)
vecs = [:x, :y, :z, :p]
for v in vecs
setproperty!(a, v, zeros(10))
end
end
init (generic function with 1 method)
julia> init(NewStruct())
As others have pointed out, this can easily be done without any meta-programming and in general, metaprogramming is just the wrong tool for this job. However, if you’re interested in learning about metaprogramming, it is often useful to learn it with simple cases like this rather than with something complicated.
The tool you want for this is called a macro. I’d strongly recommend reading this section of the documentation: Metaprogramming · The Julia Language. It’s very well written and useful.