How to set the elements of an array of defined type in one line?

I have a simple code and a simple question.
it is with type My_w, and a 2-element array www whose type is My_w,

mutable struct My_w
    w::Float64
end
www = Array{My_w,1}(undef,2)

Now, I want to set www[1] and www[2] both as My_w(1.0). Can I do it in one line like Fortran did, such as,

www[:] = My_w(1.0)

But obviously this give me an error.

Or, do I have to do a loop for such a simple initialization like below?

for i in 1:2
    www[i] = My_w(1.0)
end

Is there a one-line ‘vectorization’ flavor solution instead of writing a loop?


Furthermore, if I have define another 2 elements array A,

A=zeros(2)

Now I want to set the elements of array A as the values of www[:].w, in fortran I can do things like,

A[:] = www[:].w

But in Julia obviously this gives me an error.

Again, in Julia do I have to do

for i in 1:2
   A[i] = www[i].w
end

Is there more concise way like Fortran did?

Dear all, as you can see, the : symbol in Fortran seems very powerful actually for array operations.

Thanks in advance!

For separate elements,

julia> www .= [My_w(1.), My_w(1.)]
2-element Vector{My_w}:
 My_w(1.0)
 My_w(1.0)

To reuse the same element twice,

julia> www .= Ref(My_w(1.))

or

julia> www .= [My_w(1.)]
2-element Vector{My_w}:
 My_w(1.0)
 My_w(1.0)
1 Like

Does the Fortran code make each element reference to the same object or no? if the answer is no, then

@. ary = My_w(1.0)

if the answer is yes:

fill!(ary, My_w(1.0))

btw, it’s probably not worth to have this being mutable to begin with

1 Like

won’t work because that object has no length so it has to be wrapped with Ref or similar.

1 Like

right, need to make broadcast style Ref

Thank you very much, your solution 3 looks great!
I was trying to do =. instead of .= :sweat_smile:

I have added a furthermore paragraph, would you mind have a look? it is just that, is there a way to do things like

A[:] = www[:].w

Thanks!

PS as @jling pointed out, it seems the solution 3 makes www[1] and www[2] identical. I need to set them independently.

julia> www .= [My_w(1.)] # <---- this is assigning the same My_w to each element of www
2-element Vector{My_w}:
My_w(1.0)
My_w(1.0)

julia> www[1].w = 3
3

julia> www
2-element Vector{My_w}:
My_w(3.0)
My_w(3.0)

A .= getproperty.(www, :w)

would you explain why do you want an array of the same My_w object? what’s the point of mutable now that when you mutate one element you mutate all of them?

Also in this case because all of the My_w are the same, you can technically do

A .= first(www).w

Well, simply because I need to.
This is just a highly simplified code and so it is easy to ask a question and find a solution.

In a Gaussian mixture model for example, you have many mixtures, you can define a type, then the whole mixtures is just an array of that type, each element has its own mean value, covariance matrix, weight, etc.

Fortran module is below,

module mixture
    use constants
    implicit none  
    type :: mean_covar
	    real(kind=r8), allocatable :: mu(:,:)
	    real(kind=r8), allocatable :: sigma(:,:)
	    real(kind=r8) :: w
    end type	
    type (mean_covar), public, allocatable, save :: musigma(:)
    interface assignment (=)
	    module procedure copy_mean_covar
    end interface
!  +,-,*,/ are not defined, so do not use them for now.  		
contains	
    subroutine copy_mean_covar(mcl,mcr)
    type (mean_covar), intent(inout) :: mcl !left side of assignment. here need to use intent(inout)
    type (mean_covar), intent(in) :: mcr !right side of assignment
    mcl%mu(:,:)=mcr%mu(:,:)
    mcl%sigma(:,:)=mcr%sigma(:,:)
    mcl%w=mcr%w
    return
    end subroutine copy_mean_covar

    subroutine mean_covar_init(kmix,dim_p,weight,sigma,mu)
    integer(kind=i8) :: i,k,kmix,dim_p
    real(kind=r8) :: weight(kmix),sigma(kmix,dim_p,dim_p),mu(kmix,dim_p)
    allocate(musigma(kmix))
    do i = 1, kmix
	    allocate(musigma(i)%mu(dim_p,1),musigma(i)%sigma(dim_p,dim_p))  
	    musigma(i)%mu = zero
	    musigma(i)%sigma = zero
    enddo       
    do k=1,kmix
        musigma(k)%mu(1,1) = mu(k,1)
        musigma(k)%mu(2,1) = mu(k,2)
        musigma(k)%sigma(1,1) = sigma(k,1,1) 
        musigma(k)%sigma(2,2) = sigma(k,2,2) 
        musigma(k)%w = weight(k)            
    enddo
    return
    end subroutine mean_covar_init 

end module mixture

Then in the initialization I need to do things like,

call mean_covar_init(kmix,dim_p,weight,sigma,mu)

... 

wnow(:) = musigma(:)%w

In Fortran it is one line like above.

but right now if you change stuff in one mixture, everything in the array changes because you just have the same mixture object in that array

Uhm, not sure what you mean.
But the answer is no, each element in the array is independent, I can set the values of the fields in each element independently.
I have an array called musigma, each element in it has a type called mean_covar which is defined in this module.
But like I said again, I just simplify things so that I can ask a relevant question.

julia> www .= [My_w(1.)] # <---- this is assigning the same My_w to each element of www
2-element Vector{My_w}:
 My_w(1.0)
 My_w(1.0)

julia> www[1].w = 3
3

julia> www
2-element Vector{My_w}:
 My_w(3.0)
 My_w(3.0)

Oh, I got what you mean!
Thanks man!

So this is not what I want.

I want www[1], www[2] to be independent.

that’s why I asked:

and the answer I also had up there:

julia> @. www = My_w(1.) #same as www .= My_w.(1.)
2-element Vector{My_w}:
 My_w(1.0)
 My_w(1.0)

julia> www[1].w = 3
3

julia> www
2-element Vector{My_w}:
 My_w(3.0)
 My_w(1.0)
2 Likes

Right right right, my bad, it’s on me :sweat_smile:

The answer is of course no. There is no point to make all the elements reference to the same value all the time.

Could you please explain the @. a little bit? Sorry if it is stupid.

Also would you mind looking the my edited '‘Furthermore’ part, as below,


Furthermore, if I have define another 2 elements array A,

A=zeros(2)

Now I want to set the elements of array A as the values of www[:].w, in fortran I can do things like,

A[:] = www[:].w

But in Julia obviously this gives me an error.

Again, in Julia do I have to do

for i in 1:2
   A[i] = www[i].w
end

Is there more concise way like Fortran did?

Thanks in advance!

did you miss the getproperty reply I had above?

@. makes everything in the line broadcast, in this case you can also put in the two dots individually if you want

Got it, thanks!
Oh, by the way, I saw you have a ‘regular’ title, not sure what does that mean, but I guess it is like underboss or captain in Julia.
If so, I just have a suggestion, could you please tell the boss, is it possible in the future Julia documentation pdf https://raw.githubusercontent.com/JuliaLang/docs.julialang.org/assets/julia-1.6.2.pdf, at the end, add an index or something like many books did, so that it could be more convenient to locate to some Julia functions, commands, symbols stuff.
Currently, I am kind of lost in ctrl+F when browsing the PDF

haha no, regular just means I don’t have a life /s

That’s a good suggestion, I would suggest you to use the web interface which has search, or use the REPL ? help mode.

3 Likes

You could open an issue for that on JuliaLang/Julia though they might redirect you to Documenter.jl

1 Like

I’m surprised the comprehension constructor

www = [My_w(1) for _ in 1:2]

has not been mentioned.
For A, the same:

A = [x.w for x in www]

If this absolutely has to work with preallocated arrays, the following must work:

www .= (My_w(1) for _ in eachindex(www))
A .= (x.w for x in www)

map! may be a bit of overkill for this specific task but is a very useful and powerful concept overall:

map!(x -> My_w(1), www, eachindex(www))
map!(x -> x.w, A, www)
6 Likes