Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Copy On Modify; What Happens When You Run This Code? x <- list(1:10); x[[2]] <- x

Tags:

list

copy

r

I'm a bit puzzled by question 4 in section 3.36 of "Advanced R" (https://adv-r.hadley.nz/names-values.html). The chapter that I have linked to explains the conventions I've used in the images I attached. I'll summarize them briefly. For those not familiar with the book and those who do not feel like clicking on the link and reading the authors description of diagrams similar to mine, I've added a brief explanation of the conventions I've used in the image below the rest of this post.

What happens when you run this code? Draw a picture

x <- list(1:10)
x[[2]] <- x

I understand what is being created here. x <- list(1:10) creates a list. That list has one element that points to a vector 1:10. After x[[2]] <- x runs, x points to a list with two elements. The first element points to a vector 1:10. The second element is a reference to a list that is identical to the original list (or maybe it is not just identical to the original, but is actually the original list?). I'm just confused about which objects (not variables, objects) are being copied and/or referenced to in the new list.

Here's an attempt at a solution.

After just running x <- list(1:10), the name x is bound to a list object. That list has one reference, which is to a vector (1:10). enter image description here

Here's where I'm confused. I'm not sure what happens when x[[2]] <- x is executed. Here's my best guess, which I think is wrong. When x[[2]] <- x is executed, a copy of the object that x was originally bound to is made. For that copy, a second element is created which points to the original object.

enter image description here

I assume my interpretation of what happens when x[[2]] <- x executes is incorrect. If so, can someone help me understand what is going on? And if I happen to be correct, can someone help explain why I am correct?


In the images I've used, a rounded square represents a name/variable. A black arrow is a binding from a name to an object (opposite to the direction the assignment arrow points in R). The rectangles below represent lists when there are green circles inside. The green circles represent an element of the list. Since list elements are references to objects, the green arrows point to the object that a list element is referencing to.

In the first image, the list has one element and that element points to the vector 1:10.

In the second image, the first element of the list object points to the vector 1:10. The second element is a reference to a list.

like image 275
Dave Rosenman Avatar asked Aug 07 '18 19:08

Dave Rosenman


2 Answers

Maybe this will help. Here we use pryr::address to see the memory location where objects are stored (note, your actual addresses may vary, but when I have matching addresses, your addresses should match as well).

library(pryr)
x <- list(1:10)
pryr::address(x)
# [1] "0x3452810"
y <- x[[1]]
pryr::address(y)
# [1] "0x16b53bf0"

So we have an list x at a given location. We can think of lists in R as collections of pointers to other objects. We can't directly take the address of where it's storing it's first item (at least, I don't know how with address), but we can store that value to y and since R will only change address when objects are modified, we can assume this is where that first value is stored. Now let's update x

x[[2]] <- x
pryr::address(x)
# [1] "0x16001018"

we can see that x has changed and been given a new memory location

y <- x[[1]]
pryr::address(y)
# [1] "0x16b53bf0"

Note that the first element though is still at the same memory address. So a new copy of this vector hasn't been made. The new list just points to that same vector. Now let's look at the address of the value we just added

y <- x[[2]]
pryr::address(y)
# [1] "0x3452810"

Note that this value now points to the old memory address where the original x lived.

And further more

y <- x[[2]][[1]]
pryr::address(y)
# [1] "0x16b53bf0"

both lists point to the same 1:10 vector. It's only stored once.

So when you do x[[2]]<-x what you are doing is creating a new list. This new list contains two "pointers" essentially. One to the same vector that was in the original list, and one that points to the original address of the list.

like image 166
MrFlick Avatar answered Oct 23 '22 15:10

MrFlick


x <- list(1:10) there you create a list. Let's inspect it using str()

> str(x)
List of 1
 $ : int [1:10] 1 2 3 4 5 6 7 8 9 10

Then you assign this list to next position in the list. Such as x[[2]] <- x. Now the list x contains:

> str(x)
List of 2
 $ : int [1:10] 1 2 3 4 5 6 7 8 9 10
 $ :List of 1
  ..$ : int [1:10] 1 2 3 4 5 6 7 8 9 10

To check what's going on. I check the type of x and x[[2]]

> typeof(x) 
[1] "list"
> typeof(x[[2]])
[1] "list"

As a result, x is a list which contains a list.

Update

I used ref() function from lobstr packages.

> x <- list(1:10)
> ref(x)
o <1:0x344642b8> [list]
\-<2:0x20ad2588> [int]

> x[[2]] <- x
> ref(x)
o <1:0x34aee7e0> [list]
+-<2:0x20ad2588> [int]
\-o <3:0x344642b8> [list]
  \-<2:0x20ad2588> [int]

The result shows that the value corresponds to the same memory location.

like image 2
RobJan Avatar answered Oct 23 '22 14:10

RobJan