Hi,
I am buffled by what copy!(z,view(x,1,:)') does. Seems off. Example:
x = [11 12;111 112] #to read from
y = [ones(1,2)*1,ones(1,2)*2] #2-vector of matrices, to be pushed to
z = zeros(1,2) #preallocate z
copy!(z,view(x,1,:)') #copy first row of x to z
display(z)
pushfirst!(y,z)
copy!(z,view(x,2,:)') #copy 2nd row
display(z)
pushfirst!(y,z)
display(y)
Looks like mutation semantics to me. You don’t reassign z in the given example, just mutate its preallocated instance. y gets the same instance assigned to z pushed to its beginning twice, in short y[1] === y[2]. If you want to see different values, you do need to allocate more.
Yes, the whole idea was to (keep) mutating an existing array, so as to avoid allocations in a long loop. Still, the display(z) (twice in the example) suggests that z is changed. It’s just at the stage of pushfirst!() that it somehow isn’t.
When you pushfirst!(y, z), you’re putting the matrix named z inside y. If you do it twice, y will contain [#= the matrix named z =#, #= the matrix named z =#, ...]. If you mutate the matrix named z, then that matrix changes. And since y has that matrix placed in two locations, you’ll see that change in both those locations. Because it’s just one matrix.
If you decide to do z = x[1,:]' in between, you’re deciding to repurpose the name z for a brand spanking new matrix — and that matrix will be completely independent from anything you might have called z in the past.
To possibly further clarify what @Benny and in the mean time also @mbauman are saying, the variable z just points to some location in memory. When you mutate z using copy! (or some .=-like operation), z still points to the same location, but the values in memory have now changed.
julia> z = ones(1, 2)
1Ă—2 Matrix{Float64}:
1.0 1.0
julia> pointer(z) # Memory location where z points to
Ptr{Float64} @0x000002245d8f0900
julia> z .*= 2
1Ă—2 Matrix{Float64}:
2.0 2.0
julia> pointer(z) # Same location (but different values in memory)
Ptr{Float64} @0x000002245d8f0900
julia> z = [2. 2.] # New variable coincidentally also called z, with the same values as before
1Ă—2 Matrix{Float64}:
2.0 2.0
julia> pointer(z) # but new location
Ptr{Float64} @0x000002245da1c4a0
When you pushfirst!z into y the new first entry of y should be found at the memory location corresponding to z. Mutating z and using pushfirst! again, the first two entries both point to the same location in memory, so have the same values.
If you intend to have 2 different mutable matrices, you’ll need to allocate 2 mutable matrices because it’s the memory-safe way for references to share data, as you inadvertently ran into. If you only need the two, you can preallocate those. If you have a loop with indeterminate iterations, then you have a situation where you need to allocate frequently. Is it possible for the matrices to be immutable? That is far more feasible to store directly in y instead of a separate allocation, though you’ll probably want to preallocate elements for y because resizing needs allocation.
Thanks, I’ll follow your idea and probably set up multidimensional array y and change elements in that. (I was probably trying to be too clever with my vector of matrices…)