在 R 中切片复制修改吗?

Is slicing copy-on-modify in R?

有人提到 here R 在将变量分配给新变量时使用修改时复制,包括将参数传递给函数。

但是,切片 (vectorlistdata 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

这对 vectorslistsdataframes 是一样的。在 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.

处的新对象