3

What I am trying to do is not very straight forward, maybe it is easier if I start with the result and then explain how I am trying to get there.

I have a struct with two fields:

struct data{T}
    point::T
    mat::Array
end

What I would like to do is nest this and make the field mat self-referential to get something like this:

data{data{Int64}}(data{Int64}(1, [1]), [1])

The 'outer' type should not store [1] but reference to the innermost mat. I am not sure if this makes sense or is even possible. The field mat should store the same large array repeatedly.

I have tried something like this (n is the number of nested types.

struct data{T}
    point::T
    g::Array
    function D(f, g, n)
        for i = 1:n
            (x = new{T}(f, g); x.f = x)
        end
    end
end 

Again I am not sure if I understand self-referential constructors enough, or if this is possible. Any help/clarification would be appreciated, thanks!

2 Answers2

4

The exact pattern will depend on what you want to achieve but here is one example:

struct Data{V, A <: AbstractArray{V}, T} 
    mat::A
    point::T

    Data(mat::A, point::T = nothing) where {V, A <: AbstractArray{V}, T} =
        new{V,A,T}(mat,point)
end

Usage

julia> d0 = Data([1,2,3])
Data{Int64,Array{Int64,1},Nothing}([1, 2, 3], nothing)

julia> d1 = Data([1.0,2.0],d0)
Data{Float64,Array{Float64,1},Data{Int64,Array{Int64,1},Nothing}}([1.0, 2.0], Data{Int64,Array{Int64,1},Nothing}([1, 2, 3], nothing))

Tips:

  1. Never use untyped containers. Hence, when you want to store an Array you need to have its type in your struct defintion.

  2. Use names starting with a capital letter for structs

  3. Provide constructors to have your API readable

Last but not least. If you want to have several nesting levels for such structure the compiling times will hugely increase. In that case it would be usually better to use homogenous types. In such scenarios you could use perhaps type Unions instead (unions of small number of types are fast in Julia).

Przemyslaw Szufel
  • 40,002
  • 3
  • 32
  • 62
3

Based on your description, the data seems like a very general wrapper. Maybe you can try something like this:

mutable struct Wrap{T}
    w::Wrap{T}
    d::T
    function Wrap(d::T) where T
        w = new{T}()
        w.d = d
        w
    end
end

function Wrap(d, n::Int)
    res = Wrap(d)
    cur = res
    for _ in 1:n-1
        cur.w = Wrap(d)
        cur = cur.w
    end
    res
end

Wrap([1], 4)
# Wrap{Array{Int64,1}}(Wrap{Array{Int64,1}}(Wrap{Array{Int64,1}}(Wrap{Array{Int64,1}}(#undef, [1]), [1]), [1]), [1])
Jun Tian
  • 1,340
  • 7
  • 12
  • 1
    I would define here `w` as `w::Union{Wrap{T}, Nothing}`. Otherwise it remains in an `#undef` state for the root element which is kind of ugly. – Przemyslaw Szufel Apr 11 '20 at 10:29
  • Thanks this is close to what I was looking for, tho the type for say Wrap([1], 2) should be: Wrap{Wrap{Int64}}(Wrap{Int64}(1, [1]), [1]) – secretsanta Apr 12 '20 at 07:48