如何让R6对象的数组成员实现原地更新而非复制更新?
如何让R6对象实现数组的原地更新?
我们通过以下代码测试R6对象数组成员的更新行为:
require(R6) Array <- R6Class( "Array", public=list( x=matrix(0,0,0), initialize=function(a,b,c){ self$x <- matrix(a,b,c) }, assign=function(z){ self$x[1,1] <- z invisible(self) } ) ) x <- Array$new(0,10,10) tracemem(x$x) x$assign(1) y <- matrix(0,10,10) tracemem(y) y[1,1] <- 1
运行后得到输出:
> [1] "<0x55ae7be40040>" > tracemem[0x55ae7be40040 -> 0x55ae7b403700]: <Anonymous> > > [1] "<0x55ae7b254c90>" > >
测试结果显示:更新R6对象的数组成员时会生成数组副本,而更新普通数组时则是原地更新(即不生成副本)。
问题原因
当执行self$x[1,1] <- z时,R会先取出self$x的副本,修改副本后再重新赋值给self$x,这个过程触发了副本创建。
解决方案
要实现原地更新,需要绕过R6的成员访问器直接操作底层环境,有两种可行方式:
方式1:直接操作R6对象的环境
修改assign方法,直接从对象的环境中获取数组并修改,避免取值时生成副本:
require(R6) Array <- R6Class( "Array", public=list( x=matrix(0,0,0), initialize=function(a,b,c){ self$x <- matrix(a,b,c) }, assign=function(z){ env <- environment(self) env$x[1,1] <- z invisible(self) } ) )
方式2:使用<<-操作符
在方法内部使用<<-,它会在当前环境的父环境(即R6对象的环境)中查找变量并直接修改,无需生成副本:
require(R6) Array <- R6Class( "Array", public=list( x=matrix(0,0,0), initialize=function(a,b,c){ self$x <- matrix(a,b,c) }, assign=function(z){ x[1,1] <<- z invisible(self) } ) )
验证效果
用修改后的代码重新测试,tracemem不会输出副本创建的日志,说明实现了原地更新:
x <- Array$new(0,10,10) tracemem(x$x) x$assign(1)
原理说明
R6对象的成员存储在其对应的环境中,self$x的访问会触发一次取值操作,对这个取值结果的修改会默认生成副本。而直接操作环境变量或使用<<-,可以直接修改原始数组对象,避免副本生成。
内容的提问来源于stack exchange,提问作者user1407220
相关产品推荐
相关产品推荐

