约翰·钱伯斯(John Chambers)在《Software for Data Analysis: Programming with R》一书中强调,函数的编写通常不应考虑其副作用;相反,函数应该在不修改其调用环境中的任何变量的情况下返回值.相反,使用数据编写好的脚本.表对象应特别避免使用对象赋值<-
,通常用于存储函数的结果.
首先是一个技术问题.想象一下,一个名为proc1
的R函数接受data.table
对象x
作为其参数(可能还有其他参数).proc1
返回NULL,但使用:=
修改x
.据我所知,proc1
呼叫proc1(x=x1)
复制x1
只是因为promise 的工作方式.然而,如下所示,原始对象x1
仍然被proc1
修改.这是怎么回事?
> require(data.table)
> x1 <- CJ(1:2, 2:3)
> x1
V1 V2
1: 1 2
2: 1 3
3: 2 2
4: 2 3
> proc1 <- function(x){
+ x[,y:= V1*V2]
+ NULL
+ }
> proc1(x1)
NULL
> x1
V1 V2 y
1: 1 2 2
2: 1 3 3
3: 2 2 4
4: 2 3 6
>
此外,使用proc1(x=x1)
似乎并不比直接在x上执行程序慢,这表明我对promise 的模糊理解是错误的,它们以一种通过引用的方式工作:
> x1 <- CJ(1:2000, 1:500)
> x1[, paste0("V",3:300) := rnorm(1:nrow(x1))]
> proc1 <- function(x){
+ x[,y:= V1*V2]
+ NULL
+ }
> system.time(proc1(x1))
user system elapsed
0.00 0.02 0.02
> x1 <- CJ(1:2000, 1:500)
> system.time(x1[,y:= V1*V2])
user system elapsed
0.03 0.00 0.03
所以,考虑到传递数据.函数的table参数不会增加时间,这使得为数据编写过程成为可能.表对象,结合了数据的速度.表和函数的可推广性.然而,考虑到John Chambers所说的,函数不应该有副作用,用R编写这种类型的过程编程真的"可以"吗?他为什么说副作用是"坏的"?如果我要无视他的建议,我应该注意哪些trap ?我该怎么写"好"的数据呢.表格程序?