44

偶然我遇到了"[<-"操作员的奇怪行为。它的行为取决于调用顺序以及我使用的是 RStudio 还是普通的 RGui。我会用一个例子说明清楚。

x <- 1:10
"[<-"(x, 1, 111)
x[5] <- 123

据我所知,第一个分配不应该改变x(或者我错了?),而第二个应该改变。而实际上上述操作的结果是

x
[1]  1  2  3  4  123  6  7  8  9 10

但是,当我们以不同的顺序执行这些操作时,结果是不同的并且x已经改变了!意味深长:

x <- 1:10
x[5] <- 123
"[<-"(x, 1, 111)
x
[1] 111   2   3   4   123   6   7   8   9  10

但它只发生在我使用纯 R 时!在 RStudio 中,两个选项的行为相同。我在两台机器上检查过(一台使用 Fedora,一台使用 Win7),情况看起来完全一样。我知道'功能'版本("[<-"(x..))可能从未使用过,但我很好奇它为什么会发生。谁能解释一下?

===========================

编辑:好的,所以从评论中我知道原因是它x <- 1:10的类型为'integer'并且在替换x[5] <- 123它之后是'double'。但仍然存在疑问,为什么 RStudio 中的行为不同?我重新启动 R 会话,它没有改变任何东西。

4

1 回答 1

37

Rstudio 的行为

Rstudio 的对象浏览器修改它检查的对象,在修改时强制复制。具体来说,对象浏览器使用至少一个 R 函数,其调用在内部强制评估对象,在此过程中将对象的命名字段的值从 1 重置为 2。来自R-Internals 手册

当一个对象即将被更改时,会查询指定的字段。值 2 表示对象必须在被更改之前被复制。[...] 值 1 用于 [...] 原则上在计算期间存在两个副本的情况 [...] 但不再存在,因此可以优化一些原始函数在这种情况下避免复制。

要查看对象浏览器修改了命名字段([NAM()]在下一个代码块中),请比较运行以下行的结果。首先,两条“线”一起运行,因此 RstudioX在查询其结构之前没有时间“接触”。在第二种情况下,每一行都是单独粘贴的,所以X在检查之前进行修改。

## Pasted in together
x <- 1:10; .Internal(inspect(x))
# @46b47b8 13 INTSXP g0c4 [NAM(1)] (len=10, tl=0) 1,2,3,4,5,...

## Pasted in with some delay between lines
x <- 1:10
.Internal(inspect(x))
# @42111b8 13 INTSXP g0c4 [NAM(2)] (len=10, tl=0) 1,2,3,4,5,... 

一旦命名字段设置为 2,[<-(X, ...)将不会修改原始对象。将以下内容一次性粘贴到 Rstudio 中会修改X,而逐行粘贴则不会:

x <- 1:10
"[<-"(x, 1, 111)

所有这一切的另一个后果是,Rstudio 的对象浏览器实际上使某些操作比其他操作要慢。同样,比较首先粘贴在一起的相同的两个命令,然后一次一个:

## Pasted in together
x <- 1:5e7
system.time(x[1] <- 9L)
#    user  system elapsed 
#       0       0       0 

## Pasted in one at a time
x <- 1:5e7
system.time(x[1] <- 9L)
#    user  system elapsed 
#    0.11    0.04    0.16 

[<- 在 R 中的可变行为

[<-wrt 修改向量的行为X取决于X分配给它的元素的存储类型和存储类型。这解释了R的行为,但不解释 Rstudio 的行为。

在 R 中,当[<-附加到 vectorX或执行需要X修改其类型的子X赋值时,将被复制并且返回的值不会覆盖预先存在的变量X。(为此,您需要执行类似X <- "[<-(X, 2, 100).

所以,以下都不修改 X

X <- 1:2         ## Note: typeof(X) --> "integer"

## Subassignment that requires that X be coerced to "numeric" type
"[<-"(X, 2, 100) ## Note: typeof(100) --> "numeric"
X 
# [1]   1   2

## Appending to X
"[<-"(X, 3, 100L)
X
# [1]   1   2

不过,只要有可能,R 确实允许[<-函数通过引用直接修改X(即不复制)。这里的“可能”包括子分配不需要X修改类型的情况。

所以以下所有修改 X

X <- c(0i, 0i, 0i, 0i)
"[<-"(X, 1, TRUE)
"[<-"(X, 2, 20L)
"[<-"(X, 3, 3.14)
"[<-"(X, 4, 5+5i)
X
# [1]  1.00+0i 20.00+0i  3.14+0i  5.00+5i
于 2013-03-21T23:17:07.687 回答