我有Java和Python的背景,最近我正在学习R.
今天我发现R似乎处理的对象与Java和Python完全不同。
例如,以下代码:
x <- c(1:10)
print(x)
sapply(1:10,function(i){
x[i] = 4
})
print(x)
代码给出以下结果:
[1] 1 2 3 4 5 6 7 8 9 10
[1] 1 2 3 4 5 6 7 8 9 10
但我希望输出的第二行全部为'4',因为我修改了sapply函数中的向量。
这是否意味着R在函数调用中复制对象而不是引用对象?
答案 0 :(得分:18)
x
在全局环境中定义,而不是在您的函数中定义。
如果您尝试在函数中修改非本地对象(如x
),则R会复制该对象并修改副本,因此每次运行匿名函数时都会复制{{1}制作并将其第i个组件设置为4.当函数退出时,所制作的副本将永久消失。原始x
未修改。
如果我们要写x
或者我们写x[i] <<- i
,那么R会把它写回来。将其写回的另一种方法是将x[i] <- 4; assign("x", x, .GlobalEnv)
设置为e
存储的环境并执行此操作:
x
或者可能是这样:
e <- environment()
sapply(1:10, function(i) e$x[i] <- 4)
通常一个人不会在R中编写这样的代码。而是将结果作为函数的输出生成如下:
sapply(1:10, function(i, e) e$x[i] <- 4, e = environment())
(实际上在这种情况下,可以写x <- sapply(1:10, function(i) 4)
。)
增加:
使用proto package可以执行此操作,方法x[] <- 4
将f
属性的第i个组件设置为4。
x
增加:
在上面添加了另一个选项,我们明确地传递了library(proto)
p <- proto(x = 1:10, f = function(., i) .$x[i] <- 4)
for(i in seq_along(p$x)) p$f(i)
p$x
存储的环境。
答案 1 :(得分:7)
是的,你是对的。检查R语言定义:4.3.3 Argument Evaluation
AFAIK,R在您尝试修改数据之前并不真正复制数据,因此遵循Copy-on-write语义。
答案 2 :(得分:3)
匿名函数中的x
不全局环境中的x
(您的工作区)。它是x
的副本,是匿名函数的本地副本。说R在函数调用中复制对象并不是那么简单;如果可以的话,R会努力不复制,但是一旦修改了R就必须复制对象。
正如@DWin所指出的那样,x
已经修改<{1>}的已复制版本 已被sapply()
调用返回,您声明的输出不是我得到的:< / p>
> x <- c(1:10)
> print(x)
[1] 1 2 3 4 5 6 7 8 9 10
> sapply(1:10,function(i){
+ x[i] = 4
+ })
[1] 4 4 4 4 4 4 4 4 4 4
> print(x)
[1] 1 2 3 4 5 6 7 8 9 10
显然,代码几乎完成了你的想法。问题是sapply()
的输出没有分配给一个对象,因此被打印出来然后被丢弃。
您编码甚至工作的原因是由于R的范围规则。您确实应该将函数作为参数传递给函数所需的任何对象。但是,如果R不能找到函数本地的对象,它将在父环境中搜索与名称匹配的对象,然后在适当的情况下查找该环境的父对象,最终命中全局环境,即工作空间。因此,您的代码可以正常工作,因为它最终找到了x
可以使用,但会立即被复制,该副本会在sapply()
调用结束时返回。
在许多情况下,这种复制确实需要时间和内存。这是人们认为for
循环在R中缓慢的原因之一;在填充循环之前,它们不会为对象分配存储空间。如果不分配存储,R必须修改/复制对象以添加循环的下一个结果。
但是,R中的所有地方并不总是那么简单,例如环境,环境的副本实际上只是指原始版本:
> a <- new.env()
> a
<environment: 0x1af2ee0>
> b <- 4
> assign("b", b, env = a)
> a$b
[1] 4
> c <- a ## copy the environment to `c`
> assign("b", 7, env = c) ## assign something to `b` in env `c`
> c$b ## as expected
[1] 7
> a$b ## also changed `b` in `a` as `a` and `c` are actually the same thing
[1] 7
如果您了解这些类型的内容,请阅读R Language Definition手册,其中涵盖了R中引人注目的许多细节。
答案 3 :(得分:0)
您需要将sapply的输出分配给对象,否则它就会消失。 (实际上你可以恢复它,因为它也被分配到.Last.value
)
x <- c(1:10)
print(x)
[1] 1 2 3 4 5 6 7 8 9 10
x <- sapply(1:10,function(i){
x[i] = 4
})
print(x)
[1] 4 4 4 4 4 4 4 4 4 4
答案 4 :(得分:0)
如果要从函数中更改“全局”对象,则可以使用非本地分配。
x <- c(1:10)
# [1] 1 2 3 4 5 6 7 8 9 10
print(x)
sapply(1:10,function(i){
x[i] <<- 4
})
print(x)
# [1] 4 4 4 4 4 4 4 4 4 4
虽然在这种特殊情况下你可以像x[]<-4
顺便说一下,R的一个不错的功能 - 而不是sapply(1:10,function(i) x[i] <<- 4
或for(i in 1:10) x[i]<-4
(for
不是函数,所以你不需要{ {1}}这里你可以写<<-
:)