How to work with julia's structs

data_structures

#1

Hello,

I am Stefano, Ph.D. Student in Physics. For my research project, I need to use Julia. I come from CPP and I have not entirely changed my mental approach to this new programming language. Now I am in front of weird behavior in the data struct manipulation.

Can I define a struct method in function of others? A suitable example is the very inflated struct point.
Given its spherical coordinate (r, theta, phi) I want the automatically completion of the x, y, z method. I tried a thing like this: (just for x coordinate)

struct point{T}
  r::T 
  theta::T
  phi::T
  x::T
  function point(r,theta,phi)
    x = r * sin(theta) * cos(phi)
  end
end

But it does not work. Can someone give me a hint? Thanks!


#2

Welcome,

please take a look at PSA: how to quote code with backticks.

Regarding your problem, https://docs.julialang.org/en/v1/manual/constructors/index.html is likely good reading.

You could do for example:

julia> struct Point{T}
           r::T
           theta::T
           phi::T
           x::T
       end

julia> Point(r, theta, phi) = Point(r, theta, phi, r * sin(theta) * cos(phi))
Point

julia> Point(2.0, 0.3, 0.5)
Point{Float64}(2.0, 0.3, 0.5, 0.5186867601044616)

#3

You could also define a function x(p::Point) that takes a point as argument and calculates its x component (without storing it in the struct itself, which may not be necessary).


#4

Thank you for the answers!

I use this opportunity to ask:
From the performance point of view is better either defined a constructor or use a separate function like proposed by @dpsanders

The constructor (I call it constructor but I don’t know if it is correct…) could be a thing like this:

julia> struct point
           r
           theta
           phi
           x
           y
           z
           point(r,theta,phi,x,y,z) = new(r,theta, phi, r*sin(theta)*cos(phi), r*sin(theta)*sin(phi), r*cos(theta))
       end

What is the better solution from the point of view of performance? (with “better perfomance” i means, in term of memory usage, some tricky things that i don’t know, ecc ecc)


#5

Another possibility is a functor where the struct itself can also act as a function:

struct Point{T}
    r::T
    θ::T
    Ď•::T
end

function(p::Point)()
   x = p.r * sin(p.θ) * cos(p.ϕ) 
    y = 1.0
   return [x,y] 
end

# Point(2., 0.3, 0.5) 

Point(2., 0.3, 0.5)()

#6
  • Structs are used to create types. It is recommended to start type names with a capital letter. In this case Point instead of point.
  • By not specifying a type parameter, you may end up with a mix of datatypes. Consider specifying `struct Point{T} so that the point coordinates are either all integers or all floats.
  • By defining a struct with 6 fields instead of 3 fields you double the memory required to store such structs. This might make sense if your application has to compute the x, y, and z coordinates of the same point many times, but in general, computing those coordinates when needed should be fine.
  • Your inner constructor function requires 6 input parameters, but the last three (that is, x, y and z) are not used to compute the stored values. It would be better to only have one outer constructor function that only takes 3 parameters.

#7

It really depends on your problem. If you’ll access x, y and z repeatedly, it’s better to calculate them once and store them (avoid repeated calls to sin and cos). If you don’t always need these variables, it might be better to calculate them on demand. You can also combine these two approaches and calculate them lazily, i.e. on first invocation. If you have tons of points, then memory can become an issue.

Unless you know beforehand that this will cause performance issues, I wouldn’t really worry about performance at this point… write the code in a way you’re comfortable with (your current suggestion looks fine to me), and once your project has matured a bit (and you’ve become more proficient with Julia) you can do some performance testing and see where you need to optimize things.

(With that said, note that you can speed up that constructor a bit by reusing values of sin(theta) etc. There’s also a sincos method which calculates both sin and cos at the same time (more efficient).)