It’s easier and consistent to think of a shallow copy of a mutable data structure as a copy sharing elements/instances with the original.
julia> x = [1];
julia> x2 = copy(x);
julia> x[begin] === x2[begin]
true
julia> y = [[1]];
julia> y2 = copy(y);
julia> y[begin] === y2[begin]
true
The elements of y
and y2
both contain the same instance [1]
, so mutating that instance by reassigning its element will be reflected in both y
and y2
. This doesn’t apply to x
’s immutable elements.
julia> y[begin][begin] = 2; # mutates y[begin], not y
julia> y, y2
([[2]], [[2]])
You can mutate x
by reassigning its element, and that will not occur to the copy x2
. The same thing applies to y
:
julia> x[begin] = 2;
julia> x, x2
([2], [1])
julia> y[begin] = [3];
julia> y, y2
([[3]], [[2]])
The confusion comes from 1) failing to tell what is being mutated, and 2) failing to distinguish a matrix of scalars from a vector of vectors. A matrix works fine:
julia> function test_func2()
A=zeros(Float64, 2, 2) # makes a 2x2 matrix
B=copy(A)
for ja in 1:2, jb in 1:2
(B[ja, jb],A[ja, jb])=(1.0,2.0) # mutates B and A, not its elements
end
print(B)
print(A)
end
test_func2 (generic function with 1 method)
julia> test_func2()
[1.0 1.0; 1.0 1.0][2.0 2.0; 2.0 2.0]
but so would a vector of vectors if you mutated the outer vector instead of the inner ones, though this is much worse than a proper matrix for other reasons:
julia> function test_func3()
A=[zeros(Float64,2) for _ in 1:2]
B=copy(A)
for ja in 1:2
(B[ja],A[ja])=([1.0, 1.0],[2.0, 2.0]) # mutates B and A, not its elements
end
print(B)
print(A)
end
test_func3 (generic function with 1 method)
julia> test_func3()
[[1.0, 1.0], [1.0, 1.0]][[2.0, 2.0], [2.0, 2.0]]
I assume it’s written this way for the purpose of a MWE, there are much better ways to make arrays filled with the same number.