gpt4 book ai didi

r - 向量上的修改时复制语义不会附加在循环中。为什么?

转载 作者:行者123 更新时间:2023-12-04 03:56:20 25 4
gpt4 key购买 nike

听起来这个问题可以得到部分回答here,但这对我来说还不够。我想更好地了解何时通过引用更新对象以及何时复制对象。

最简单的例子是向量增长。下面的代码在R中效率极低,因为在循环之前未分配内存,并且每次迭代都进行复制。

  x = runif(10)
y = c()

for(i in 2:length(x))
y = c(y, x[i] - x[i-1])

分配内存可以保留一些内存,而无需在每次迭代时重新分配内存。因此,此代码大大加快了速度,尤其是对于长向量而言。
  x = runif(10)
y = numeric(length(x))

for(i in 2:length(x))
y[i] = x[i] - x[i-1]

这是我的问题。实际上,当矢量更新时,它确实会移动。副本如下所示。
a = 1:10
pryr::tracemem(a)
[1] "<0xf34a268>"
a[1] <- 0L
tracemem[0xf34a268 -> 0x4ab0c3f8]:
a[3] <-0L
tracemem[0x4ab0c3f8 -> 0xf2b0a48]:

但是在循环中不会出现此副本
y = numeric(length(x))
for(i in 2:length(x))
{
y[i] = x[i] - x[i-1]
print(address(y))
}


[1] "0xe849dc0"
[1] "0xe849dc0"
[1] "0xe849dc0"
[1] "0xe849dc0"
[1] "0xe849dc0"
[1] "0xe849dc0"
[1] "0xe849dc0"
[1] "0xe849dc0"
[1] "0xe849dc0"

我了解为什么代码会根据内存分配而变慢还是变快,但是我不理解R逻辑。对于同一条语句,为什么和如何进行更新(如果引用是通过引用进行更新,而对于另一种情况,则是通过副本进行更新)。在一般情况下,我们如何知道会发生什么。

最佳答案

这在Hadley的Advanced R书中有介绍。他在其中说(这里解释),每当2个或多个变量指向同一个对象时,R会复制一个副本,然后修改该副本。在进行示例之前,Hadley的书中还提到了一个重要说明,即当您使用RStudio

the environment browser makes a reference to every object you create on the command line.



考虑到您观察到的行为,我假设您使用的是 RStudio,我们将看到这将解释为什么实际上有2个变量指向 a而不是您可能期望的1个变量。

我们将用来检查指向一个对象的变量的函数是 refs()。在您发布的第一个示例中,您可以看到:
library(pryr)
a = 1:10
refs(x)
#[1] 2

这表明(您发现的结果)有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函数是原始函数,例如 -,例如: y[i] = y[i] - y[i-1] R可以对其进行优化以避免复制。

感谢@duckmayr帮助解释了for循环内的行为。

关于r - 向量上的修改时复制语义不会附加在循环中。为什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48230311/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com