What is the point of `copy`?
Asked Answered
L

1

5

I am very confused about the purpose of copy. As illustrated in this post, copy creates a shallow copy whereas deepcopy creates an independent object. If you used copy, the underlying references remain unchanged. If I did b = copy(a) and altered a, then b would change as well.

Then what is the purpose of copy? We already have =. Is there something that copy can do but = cannot do?

Lakeesha answered 24/11, 2022 at 15:7 Comment(11)
I don't know the language but maybe array b = copy(array a) differs from array b = array a when adding new elements?Hocuspocus
Your statement is incorrect. If you alter a, b will stay unchanged.Ir
@DNF, No! Example.Rintoul
@Rintoul Actually @Ir is not entirely wrong. Try a = ones(3); b = copy(a); a[3] = 111. Why is there such a difference between the link and this example?Lakeesha
@user1691278, I guess the point is the outer and the inner structure in that case.Rintoul
Does this answer your question? Making sense of how Julia copies variablesNutt
@Rintoul My statement is correct. If you mutate an element of a, b can be changed (in those cases where this is possible), but if you alter a itself by replacing an element, b does not see this.Ir
Your statement is incorrect.. I said No, because of this sentence.Rintoul
@Rintoul I stand by my statement. I won't continue beyond this point.Ir
@Ir I didn't ask to either.Rintoul
youtu.be/yeaTNPKqRToTourer
M
7

The documentation says:

copy(x)

Create a shallow copy of x: the outer structure is copied, but not all internal values. For example, copying an array produces a new array with identically-same elements as the original.

So if you do b = copy(a), then replace an element in b, a's contents are unchanged, because it's a different object. If you just did b = a, they'd both refer to the same array, and any replacement would show up regardless of whether you looked in a or b.

Example:

> a = [1, 2]
2-element Array{Int64,1}:
 1
 2

> b = a
2-element Array{Int64,1}:
 1
 2

> c = copy(a)
2-element Array{Int64,1}:
 1
 2

> a[1] = 42
42

> a
2-element Array{Int64,1}:
 42
  2

> b
2-element Array{Int64,1}:
 42
  2

> c
2-element Array{Int64,1}:
 1
 2

> 

In the above, a refers to an array with [1, 2] in it (to start with). b is just another variable referring to the same array, but c is a shallow copy — a different array with (initially) the same elements in it. When we replace the 1 in a[1] with 42, we see that replacement whether we look through a or b because they're both looking at the same object, but c is a different object and is unaffected.


In a comment you've asked:

Why does this differ from the chosen answer in the link in my post?

The answer you refer to isn't modifying the top-level array (a) that we're either assigning to b (b = a) or copying (b = copy(a)). Since it's modifying the contents of an array within it, you see that modification.

Here's a conceptual picture of memory after a = [1, 2]:

          +−−−−−−−−−−−−−+
a−−−−−−−−>|   (Array)   |
          +−−−−−−−−−−−−−+
          | Index 1: 1  |
          | Index 2: 2  |
          +−−−−−−−−−−−−−+

Then after b = a:

a−−−−+
     |    +−−−−−−−−−−−−−+
     +−−−>|   (Array)   |
     |    +−−−−−−−−−−−−−+
b−−−−+    | Index 1: 1  |
          | Index 2: 2  |
          +−−−−−−−−−−−−−+

Then after c = copy(a):

a−−−−+    
     |    +−−−−−−−−−−−−−+
     +−−−>|   (Array)   |
     |    +−−−−−−−−−−−−−+
b−−−−+    | Index 1: 1  |
          | Index 2: 2  |
          +−−−−−−−−−−−−−+

          +−−−−−−−−−−−−−+
c−−−−−−−−>|   (Array)   |
          +−−−−−−−−−−−−−+
          | Index 1: 1  |
          | Index 2: 2  |
          +−−−−−−−−−−−−−+

Then after a[1] = 42:

a−−−−+    
     |    +−−−−−−−−−−−−−+
     +−−−>|   (Array)   |
     |    +−−−−−−−−−−−−−+
b−−−−+    | Index 1: 42 |
          | Index 2: 2  |
          +−−−−−−−−−−−−−+

          +−−−−−−−−−−−−−+
c−−−−−−−−>|   (Array)   |
          +−−−−−−−−−−−−−+
          | Index 1: 1  |
          | Index 2: 2  |
          +−−−−−−−−−−−−−+

In contrast, the answer you refer to was dealing with an array of arrays:

# The `a`, `b`, an `c` from the other answer (without the [4,5,6] array)
          
          +−−−−−−−−−−−−−−+
a−−−−−−−−>|   (Array)    |
          +−−−−−−−−−−−−−−+      
          | Index 1:     |−−−−−+
          | Index 2: ... |     |
          +−−−−−−−−−−−−−−+     |
                               |
                               |     +−−−−−−−−−−−−−−+
                               +−−−−>|   (Array)    |
                               |     +−−−−−−−−−−−−−−+
          +−−−−−−−−−−−−−−+     |     | Index 1: 1   |
b−−−−−−−−>|   (Array)    |     |     | Index 2: 2   |
          +−−−−−−−−−−−−−−+     |     | Index 3: 3   |
          | Index 1:     |−−−−−+     +−−−−−−−−−−−−−−+
          | Index 2: ... |            
          +−−−−−−−−−−−−−−+

          +−−−−−−−−−−−−−−+      
c−−−−−−−−>|   (Array)    |      
          +−−−−−−−−−−−−−−+           +−−−−−−−−−−−−−−+
          | Index 1:     |−−−−−−−−−−>|   (Array)    |
          | Index 2: ... |           +−−−−−−−−−−−−−−+
          +−−−−−−−−−−−−−−+           | Index 1: 1   |
                                     | Index 2: 2   |
                                     | Index 3: 3   |
                                     +−−−−−−−−−−−−−−+

So when they did a[1][1] = 111, it changed the one a and b were (indirectly) pointing to, but not the one c was pointing to:

# The `a`, `b`, an `c` from the other answer (without the [4,5,6] array)
          
          +−−−−−−−−−−−−−−+
a−−−−−−−−>|   (Array)    |
          +−−−−−−−−−−−−−−+      
          | Index 1:     |−−−−−+
          | Index 2: ... |     |
          +−−−−−−−−−−−−−−+     |
                               |
                               |     +−−−−−−−−−−−−−−+
                               +−−−−>|   (Array)    |
                               |     +−−−−−−−−−−−−−−+
          +−−−−−−−−−−−−−−+     |     | Index 1: 111 |
b−−−−−−−−>|   (Array)    |     |     | Index 2: 2   |
          +−−−−−−−−−−−−−−+     |     | Index 3: 3   |
          | Index 1:     |−−−−−+     +−−−−−−−−−−−−−−+
          | Index 2: ... |            
          +−−−−−−−−−−−−−−+

          +−−−−−−−−−−−−−−+      
c−−−−−−−−>|   (Array)    |      
          +−−−−−−−−−−−−−−+           +−−−−−−−−−−−−−−+
          | Index 1:     |−−−−−−−−−−>|   (Array)    |
          | Index 2: ... |           +−−−−−−−−−−−−−−+
          +−−−−−−−−−−−−−−+           | Index 1: 1   |
                                     | Index 2: 2   |
                                     | Index 3: 3   |
                                     +−−−−−−−−−−−−−−+
Murrain answered 24/11, 2022 at 15:14 Comment(3)
Follow up question: if I did a = ones(3); b = copy(a); a[3] = 111, b would not change. Why does this differ from the chosen answer in the link in my post?Lakeesha
Assuming you mean #60018033 , the important thing there is that a is a container of containers. b = copy(a) is a new outer container, and replacing a[1] will not affect b, exactly as above. Both examples also have a[2] === b[2], but in that one, these inner objects are themselves mutable containers, which are shared.Eve
@Lakeesha - I've updated my answer with a contrast between them and with diagrams.Murrain

© 2022 - 2024 — McMap. All rights reserved.