R如何在函数调用中处理对象?

时间:2011-09-24 05:37:17

标签: r object

我有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在函数调用中复制对象而不是引用对象?

5 个答案:

答案 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[] <- 4f属性的第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] <<- 4for(i in 1:10) x[i]<-4for不是函数,所以你不需要{ {1}}这里你可以写<<- :)