较早的问题(例如here)解释了 R 何时执行复制的一些方面。但是,在 OP 的示例中还有一个额外的细微差别值得在这里强调。请注意,以下代码已在 R 控制台中执行,而不是在 RStudio 中。正如其他人 previously 指出的那样,Rstudio 对流程有其自身的影响,我们在此通过直接在 R 中工作来消除这些影响。
R 的“修改行为复制”可能很复杂。 R 将尝试在适当的位置进行替换,尽可能。但是,有一个有趣的原因是,这不是它可以这样做的情况之一。这与自 R 3.5.0 以来引入的“AltRep”(替代表示)优化有关。一种这样的优化是能够仅使用它们的末端成员来存储连续的数字序列,而不是分配整个向量。详情请见here。
让我们尝试看看在这种情况下发生了什么,使用.Internal(inspect(z)) 来窥探对象表示的内部结构,并使用tracemem 来检测对象何时被复制:
x = 1L:10L
tracemem(x)
# [1] "<0x559377dba830>"
.Internal(inspect(x))
# @559377dba830 13 INTSXP g0c0 [NAM(7),TR] 1 : 10 (compact)
正如我们所见,在这个阶段,1:10 以 1:10(紧凑)的形式表示。这意味着尚未明确评估或分配完整序列。目前仅存在其开始值和结束值的紧凑表示..
现在,当我们分配给向量的一个元素时,我们看到该对象确实被复制了:
x[[3]] = 4L
# tracemem[0x559377dba830 -> 0x559376ae1e48]:
我们还可以看到,对象的内部结构也变成了一个明确的整数向量,而不是 1:10 的“紧凑”形式:
.Internal(inspect(x))
# @559376ae1e48 13 INTSXP g0c4 [NAM(1),TR] (len=10, tl=0) 1,2,4,4,5,...
现在,将其与以下版本进行比较,在该版本中, 不会触发副本(因为初始向量已经处于展开(非紧凑)形式):
x = c(1L:10L)
tracemem(x)
# [1] "<0x55d6146772d8>"
.Internal(inspect(x))
# @55d6146772d8 13 INTSXP g0c4 [NAM(1),TR] (len=10, tl=0) 1,2,3,4,5,...
x[[3]] = 4L
# No copying occured here!!