一、值传递
在R语言中,一切皆为对象,同时在调用函数的时候也采用值传递的方式,即作为参数的对象会被复制,然后将副本传递给函数
例如:
> f<-function(df2){
+ df2$a<-c(1,2,3)
+ }
> df<-data.frame(a=c(4,5,6))
> f(df)
> df
a
1 4
2 5
3 6
这里将数据框作为参数传递给函数的时候,函数内部的修改将不会影响原对象。这是因为调用f()函数时采用的时值传递的方法;df2中保持的不是指向原始数据df的引用,而是复制数据框df,df2指向的是副本数据框
如果我们想让函数内部的改动影响到函数外部,则需要在函数f()内部用返回值修改语句,然后再将函数的返回值的修改结果赋值给原来的变量
例如:
> f<-function(df2){
+ df2$a<-c(1,2,3)
+ return(df2)
+ }
> df<-data.frame(a=c(4,5,6))
> df<-f(df)
> df
a
1 1
2 2
3 3
因此我们可以得出结论,除了特殊对象以外,对象状态不会被函数直接修改,无论哪种函数能能保证参数传递的对象不被修改
PS:这一部分我觉得结合C++中指针那一块的内容食用更佳
二、对象不变性
R中的对象通常是不变的,在编程中,值不变意味着数值不能被修改
例如:
> a<-list()
> a$b<-c(1,2,3)
这则代码实际上进行的步骤是先复制a,创建新对象a',然后向对象a'添加属性b,再将c(1,2,3)填充到b,最后让变量名指向a'。
而并非在a中直接开辟一个新属性b,让后将c(1,2,3)复制给b
我们利用对象复制追踪函数tracemem()进行查看
> a<-list()
> tracemem(a)
[1] "<000001D6746E3A80>"
> tracemem(a$b<-c(1,2,3))
[1] "<000001D6771B61E8>"
我们可以发现内存地址进行了更改,意味着其进行了复制导致内存地址增加
所以在R语言里使用循环语句是很不明智的,因为会导致内存占用不断增加吃掉太多资源,从而让效率降低,因此我们还是推荐使用向量来代替实现循环语句
例如我们要实现1~1000000全体加1
循环语句
> v<-1:1000000
> for(i in 1:1000000)
+ v[i]<-v[i]+1
执行完毕后会发现内存急速扩大
利用向量
> v<-1:1000000
> v<-v+1
这样会快很多,而且占用资源更低