Hadley 的《Advanced R》一书中对此进行了介绍。在其中他说(这里解释一下)每当两个或多个变量指向同一个对象时,R 将创建一个副本,然后修改该副本。在进入示例之前,哈德利的书中也提到了一个重要的注意事项,即当您使用RStudio
环境浏览器会引用您在命令行上创建的每个对象。
鉴于您观察到的行为,我假设您正在使用RStudio
我们将看到这将解释为什么实际上有 2 个变量指向a
而不是您可能期望的 1。
我们将用来检查有多少变量指向一个对象的函数是refs()
。在您发布的第一个示例中,您可以看到:
library(pryr)
a = 1:10
refs(x)
#[1] 2
这表明(这就是你发现的)有两个变量指向a
因此任何修改a
将导致 R 复制它,然后修改该副本。
检查for loop
我们可以看到y
总是有相同的地址refs(y) = 1
在 for 循环中。y
没有被复制,因为没有其他引用指向y
在你的函数中y[i] = x[i] - x[i-1]
:
for(i in 2:length(x))
{
y[i] = x[i] - x[i-1]
print(c(address(y), refs(y)))
}
#[1] "0x19c3a230" "1"
#[1] "0x19c3a230" "1"
#[1] "0x19c3a230" "1"
#[1] "0x19c3a230" "1"
#[1] "0x19c3a230" "1"
#[1] "0x19c3a230" "1"
#[1] "0x19c3a230" "1"
#[1] "0x19c3a230" "1"
#[1] "0x19c3a230" "1"
另一方面,如果引入一个非原始的的函数y
在你的for loop
你会看到这个地址y
每次都会发生变化,这更符合我们的期望:
is.primitive(lag)
#[1] FALSE
for(i in 2:length(x))
{
y[i] = lag(y)[i]
print(c(address(y), refs(y)))
}
#[1] "0x19b31600" "1"
#[1] "0x19b31948" "1"
#[1] "0x19b2f4a8" "1"
#[1] "0x19b2d2f8" "1"
#[1] "0x19b299d0" "1"
#[1] "0x19b1bf58" "1"
#[1] "0x19ae2370" "1"
#[1] "0x19a649e8" "1"
#[1] "0x198cccf0" "1"
注意强调非原始的。如果你的函数是y
是原始的,例如-
like: y[i] = y[i] - y[i-1]
R 可以对此进行优化以避免复制。
感谢@duckmayr 帮助解释了 for 循环内的行为。