3

I am writing code in Julia which collects some output from a function foo (which mutates its input argument), and I'm trying to append the recursive evaluations from this function in an array A.

For instance, foo!(x) changes the value of x by adding 1 to each of its elements.

function foo!(x)
    x .= x .+ 1
    return(x)
end


julia> x = [1, 1];

julia> foo!(x);

julia> x
2-element Array{Int64,1}:
 2
 2

I want to create an array A which stores the value of x = f(x) over a fixed range. However, A just ends up containing multiple copies of the final value of f(x), e.g.,

julia> x = [1, 1];

julia> A = [x];

julia> for i=1:3
           push!(A, foo!(x))
       end

julia> A
4-element Array{Array{Int64,1},1}:
 [4, 4]
 [4, 4]
 [4, 4]
 [4, 4]

I'm trying to get it to get it to efficiently output something similar to

julia> B
4-element Array{Array{Int64,1},1}:
 [1, 1]
 [2, 2]
 [3, 3]
 [4, 4]

I haven't been able to find a helpful resources for a developing a solid understanding of mutations, or the order in which mutations are executed in Julia. Any help in this regard would be greatly appreciated!

SidV
  • 45
  • 4
  • Aside: It is idiomatic to write `return x` instead of `return(x)`, since `return` isn't a function, but a keyword. – DNF Apr 06 '21 at 07:03

2 Answers2

2

The way you've written it, you repeatedly push! the same object into A, which your foo! function mutates:

julia> x = [1, 1]
2-element Vector{Int64}:
 1
 1

julia> A = [x]
1-element Vector{Vector{Int64}}:
 [1, 1]

julia> foo!(x)
2-element Vector{Int64}:
 2
 2

julia> A
1-element Vector{Vector{Int64}}:
 [2, 2]

One way of fixing this is to copy the elements in A before x gets mutated:

julia> for i ∈ 1:3
           A[i] = copy(x)
           push!(A, foo!(x))
       end

julia> A
4-element Vector{Vector{Int64}}:
 [1, 1]
 [2, 2]
 [3, 3]
 [4, 4]

A classic read on values vs. bindings can be found here.

Nils Gudat
  • 13,222
  • 3
  • 39
  • 60
  • Thank you very much for the reference and the solution! This worked like a charm (using `deepcopy()` in my final implementation), and I have accepted the answer. – SidV Apr 06 '21 at 05:16
1

By using only push! you are just creating an array of references to a single array (ie x). This is why you see the same value repeated many times.

If you want to keep copies of the value of x across invocations of foo! you can use copy:

julia> foo!(x) = x .+= 1
foo! (generic function with 1 method)

julia> x = [0,0];

julia> A = [copy(foo!(x)) for i in 1:4]
4-element Vector{Vector{Int64}}:
 [1, 1]
 [2, 2]
 [3, 3]
 [4, 4]

David Sainez
  • 6,446
  • 1
  • 22
  • 45