在 R 中切片复制修改吗?
Is slicing copy-on-modify in R?
有人提到 here R
在将变量分配给新变量时使用修改时复制,包括将参数传递给函数。
但是,切片 (vector
、list
、data frame
) 会创建一个相同类型的新对象,该对象包含原始对象子集的 oopies,或者新对象中存储的元素是原始对象的副本还是修改时复制引用?
可能与 python
不同,R
会在您切片时创建新对象。例如:
> a=c(1,2,3,4,5)
> a
[1] 1 2 3 4 5
> b=a[1]
> b
[1] 1
> b=7
> b
[1] 7
> a
[1] 1 2 3 4 5
这对 vectors
、lists
或 dataframes
是一样的。在 R
.
中查看参考对象的 this post
这是一个复杂的话题。您应该从阅读 NAMED mechanism.
开始
如果您 运行 以下内容,您会发现没有列表元素的副本(因为列表基本上是指向其元素的指针):
> a <- list(1, 2, 3, 4, 5)
>
> b <- a[1:2]
> .Internal(inspect(b))
@0x000000001327e5b8 19 VECSXP g0c2 [NAM(3)] (len=2, tl=0)
@0x00000000136f6b60 14 REALSXP g0c1 [NAM(3)] (len=1, tl=0) 1
@0x00000000136f6b28 14 REALSXP g0c1 [NAM(3)] (len=1, tl=0) 2
>
>
> c <- a[1:2]
> .Internal(inspect(c))
@0x000000001327e678 19 VECSXP g0c2 [NAM(3)] (len=2, tl=0)
@0x00000000136f6b60 14 REALSXP g0c1 [NAM(3)] (len=1, tl=0) 1
@0x00000000136f6b28 14 REALSXP g0c1 [NAM(3)] (len=1, tl=0) 2
>
> b[1] <- 6
> .Internal(inspect(b))
@0x000000001327e6f8 19 VECSXP g0c2 [NAM(1)] (len=2, tl=0)
@0x0000000013745b58 14 REALSXP g0c1 [] (len=1, tl=0) 6
@0x00000000136f6b28 14 REALSXP g0c1 [NAM(3)] (len=1, tl=0) 2
>
> .Internal(inspect(c))
@0x000000001327e678 19 VECSXP g0c2 [NAM(3)] (len=2, tl=0)
@0x00000000136f6b60 14 REALSXP g0c1 [NAM(3)] (len=1, tl=0) 1
@0x00000000136f6b28 14 REALSXP g0c1 [NAM(3)] (len=1, tl=0) 2
如果您对矢量进行子集化,则情况会有所不同。
您可能还对 new reference counting mechanism 感兴趣。
将一个原子向量子集化为一个较短的向量将为您提供一个新的向量。从对象中子集化整个向量可以为您提供修改时复制参考。这样做的结果是您可以子集化以获得一个新的较短的列表对象,但它的内容将引用原始列表中的内容(没有总内存成本),直到您修改。
有关内存管理的更多详细信息,请参阅 Hadley's notes。
当对向量进行子集化时,R 不会执行 copy-on-modify ,但它会从我们正在子集化的对象中创建另一个对象。例如:
library(lobstr)
> x = rnorm(10)
# tracking object copy:
> tracemem(x)
[1] "<0xcad0728>"
> y = x # shared binding
> ref(x, y)
[1:0xcad0728] <dbl>
[1:0xcad0728]
# So, x and y points/refers to the same object at 0xcad0728. That is, we have a shared binding to the same object
# Now let's subsetting:
> y = y[1:5] # no warning from tracemem(). So, no copy-on-modify behavior triggered
# now let's check the memory addrs:
> obj_addr(x)
[1] "0xcad0728"
> obj_addr(y)
[1] "0xcd1e198"
正如我们所见,tracemem() 函数没有关于对象复制的信号。但是,subsetting 确实在内存中创建了另一个对象,因此名称 y 不再指向 0xcad0728 , 但到 0xcad0728.
处的新对象
有人提到 here R
在将变量分配给新变量时使用修改时复制,包括将参数传递给函数。
但是,切片 (vector
、list
、data frame
) 会创建一个相同类型的新对象,该对象包含原始对象子集的 oopies,或者新对象中存储的元素是原始对象的副本还是修改时复制引用?
可能与 python
不同,R
会在您切片时创建新对象。例如:
> a=c(1,2,3,4,5)
> a
[1] 1 2 3 4 5
> b=a[1]
> b
[1] 1
> b=7
> b
[1] 7
> a
[1] 1 2 3 4 5
这对 vectors
、lists
或 dataframes
是一样的。在 R
.
这是一个复杂的话题。您应该从阅读 NAMED mechanism.
开始如果您 运行 以下内容,您会发现没有列表元素的副本(因为列表基本上是指向其元素的指针):
> a <- list(1, 2, 3, 4, 5)
>
> b <- a[1:2]
> .Internal(inspect(b))
@0x000000001327e5b8 19 VECSXP g0c2 [NAM(3)] (len=2, tl=0)
@0x00000000136f6b60 14 REALSXP g0c1 [NAM(3)] (len=1, tl=0) 1
@0x00000000136f6b28 14 REALSXP g0c1 [NAM(3)] (len=1, tl=0) 2
>
>
> c <- a[1:2]
> .Internal(inspect(c))
@0x000000001327e678 19 VECSXP g0c2 [NAM(3)] (len=2, tl=0)
@0x00000000136f6b60 14 REALSXP g0c1 [NAM(3)] (len=1, tl=0) 1
@0x00000000136f6b28 14 REALSXP g0c1 [NAM(3)] (len=1, tl=0) 2
>
> b[1] <- 6
> .Internal(inspect(b))
@0x000000001327e6f8 19 VECSXP g0c2 [NAM(1)] (len=2, tl=0)
@0x0000000013745b58 14 REALSXP g0c1 [] (len=1, tl=0) 6
@0x00000000136f6b28 14 REALSXP g0c1 [NAM(3)] (len=1, tl=0) 2
>
> .Internal(inspect(c))
@0x000000001327e678 19 VECSXP g0c2 [NAM(3)] (len=2, tl=0)
@0x00000000136f6b60 14 REALSXP g0c1 [NAM(3)] (len=1, tl=0) 1
@0x00000000136f6b28 14 REALSXP g0c1 [NAM(3)] (len=1, tl=0) 2
如果您对矢量进行子集化,则情况会有所不同。
您可能还对 new reference counting mechanism 感兴趣。
将一个原子向量子集化为一个较短的向量将为您提供一个新的向量。从对象中子集化整个向量可以为您提供修改时复制参考。这样做的结果是您可以子集化以获得一个新的较短的列表对象,但它的内容将引用原始列表中的内容(没有总内存成本),直到您修改。
有关内存管理的更多详细信息,请参阅 Hadley's notes。
当对向量进行子集化时,R 不会执行 copy-on-modify ,但它会从我们正在子集化的对象中创建另一个对象。例如:
library(lobstr)
> x = rnorm(10)
# tracking object copy:
> tracemem(x)
[1] "<0xcad0728>"
> y = x # shared binding
> ref(x, y)
[1:0xcad0728] <dbl>
[1:0xcad0728]
# So, x and y points/refers to the same object at 0xcad0728. That is, we have a shared binding to the same object
# Now let's subsetting:
> y = y[1:5] # no warning from tracemem(). So, no copy-on-modify behavior triggered
# now let's check the memory addrs:
> obj_addr(x)
[1] "0xcad0728"
> obj_addr(y)
[1] "0xcd1e198"
正如我们所见,tracemem() 函数没有关于对象复制的信号。但是,subsetting 确实在内存中创建了另一个对象,因此名称 y 不再指向 0xcad0728 , 但到 0xcad0728.
处的新对象