I have an struct with some fields. Based on the types provided by the user at construction, I want the struct to remember certain fields of interest. I achieved this this way
mutable struct Foo{T}
a::T
b::T
c::Array{T,1}
fields::Array{Symbol,1}
end
For now, let’s assume that the interesting fields are always a and c.
function Foo(a,b,c)
Foo(a,b,c,[:a,:c])
end
function get_interesting_fields(foo::Foo)
reduce(vcat, [getfield(foo, f) for f in foo.fields])
end
What I want is to get a vector filled with a and the values in c. However this function is type unstable. Is there a better way to achieve this ?
I also would like to be able to change the value of the fields in the list. For example
function make_zeros(foo::Foo{T}) where T
for f in foo.fields
setfield!(foo, f, zero(T))
end
end
a
is a value of type T
, c
is a Vector{T}
, a vector of values each of type T
, you want to obtain a new vector v = [a, c...]
that is
v = [a, c[1], c[2], .. c[end]]
.
function cull_info(x)
return [x.a, x.c...]
end
myinfo = Foo(a,b,c)
v = cull_info(myinfo)
Oh yes I shoudl have specified, I must somehow iterate foo.fields because it may not always contain :a
and :c
.
Do you always know the names of the fields of interest at the time you want to cull information from them, or do you always know the index into the field sequence (1 for the field that appears first in the struct …)? Or do you mean use a
and c
when present otherwise use either if present otherwise nothing
?
At construction, Foo checks the type of the arguments a
, if it’s a Union{Real, Vector{<:Real}},
:a` is pushed to the Symbol list. Then it does the same for b and c. So I guess I always know the names of the fields, but implicitly I can derive the index too I suppose.
Is this correct?
#=
a, b, and c are obtained
a, b, and/or c may or may not be a Real value or a vector of Real values
for each of a, b, c
if it is of a type T <: Union{Real, Vector{Real}}
then you want to save the field name as a symbol
and that is how the value of your field `fields` is developed
---
at some other processing
you receive (are passed, or otherwise obtain)
an previously valued realization of the struct type (an instance)
and from that you wish to cull, as a single vector,
all values that appear in the fields that are named as symbols in `fields`
in order of those named symbols
---
=#
That we can do. 
I am out for some hours now.
Either when I return, or before then (by another person).
#=
TheStruct is the name of the structured type
thestruct = TheStruct(args...)
thestruct is the name of one realization of TheStruct
with each realization of `TheStruct`
the parameter `T1` becomes attached to a concrete type
this is the type of the first field of TheStruct
the parameter `T2` becomes attached to a concrete type
this is the type of the second field of TheStruct
=#
struct TheStruct{T1, T2}
a_field::T1
b_field::T2
fields_of_reals::Vector{Symbol}
end
const TheStruct_fieldnames = fieldnames(TheStruct)
#=
The default constructor for TheStruct requires three arguments,
which is clumsy. We specify a dispatching constructor:
=#
function TheStruct(args...)
types_of_fields = typeof.(args)
itr = zip(TheStruct_fieldnames, types_of_fields)
real_fields = Symbol[]
for (name,T) in itr
if real_or_realvec(T)
push!(real_fields, name)
end
end
return TheStruct(args..., real_fields)
end
real_or_realvec(x) = eltype(x) <: Real
#= ~~~~~~~~~~~~ =#
a = 1618
b = [1.0, 2.0]
c = "c"
struct1 = TheStruct(a, b)
TheStruct{Int64,Array{Float64,1}}(1618, [1.0, 2.0], Symbol[:a_field, :b_field])
struct2 = TheStruct(a, c)
TheStruct{Int64,String}(1618, "c", Symbol[:a_field])
struct3 = TheStruct(c, a)
TheStruct{String,Int64}("c", 1618, Symbol[:b_field])
struct4 = TheStruct(c, c)
TheStruct{String,String}("c", "c", Symbol[])
2 Likes
This is why I really love Julia. Thank you so much !