定义调用 R6 对象之外的其他方法的方法

Defining methods that call other methods outside of R6 objects

使用 R6 classes 时,在 class 之外定义调用其他方法的方法的正确方法是什么?

考虑以下示例,其中函数 func 如果以交互方式使用,可能会分派给另一个函数。但是,如果这样做,其他功能将无法访问私有环境。如果我以这种方式定义 classes,我应该传递一个环境吗?

## General function defined outside R6 class
func <- function(x) {
  if (missing(x) && interactive()) {
    ifunc()
  } else {
    private$a <- x * x
  }
}

## If interactive, redirect to this function
ifunc <- function() {
  f <- switch(menu(c('*', '+')), '1'=`*`, '2'=`+`)
  private$a <- f(private$a, private$a)
}

## R6 test object
Obj <- R6::R6Class("Obj",
  public=list(
    initialize=function(a) private$a <- a,
    geta=function() private$a,
    func=func  # defined in another file
  ),
  private=list(
    a=NA
  )
)

## Testing
tst <- Obj$new(5)
tst$func(3)
tst$geta()  # so func sees 'private'
# [1] 9

tst$func()  # doesn't see 'private'

Error in ifunc() (from #3) : object 'private' not found

您面临的问题是您定义 funcifunc 的方式使得实现仅在 class 定义内有意义 - 但并非完全如此。您收到一个错误,因为 ifunc 的实现就好像它知道您的 class 的内部结构(它指的是私有的),但事实并非如此。您实际上从未将其包含在 class 定义中。所以你必须在 func 中引用私有成员函数 ifunc 并将 ifunc 包含到 class:

func <- function(x) {
    if (missing(x) && interactive()) {
        private$ifunc()
    } else {
        private$a <- x * x
    }
}


ifunc <- function() {
    f <- switch(menu(c('*', '+')), '1'=`*`, '2'=`+`)
    private$a <- f(private$a, private$a)
}


Obj <- R6::R6Class(
    "Obj",
    public=list(
        initialize=function(a) private$a <- a,
        geta=function() private$a,
        func=func  # defined in another file
    ),
    private=list(
        a=NA,
        ifunc=ifunc
    )
)

但是,我必须说我不明白这个策略。函数 funcifunc 然后在顶级环境中都有自己的名称但不起作用 - 它们实际上只在 class 定义中才有意义。如果您对代码重用感兴趣,我认为对象组合或继承就不那么令人惊讶了。

首先,让我们把这个例子提炼一下,让它更有说服力:

# function defined outside R6 class
parent_function <- function(x) {
  if (missing(x)) {
    child_function()
  } else {
    self$a <- x * x
  }
}

# function called by previous function
child_function <- function() {
  self$a <- 999999
}

# R6 test object
my_object <- R6::R6Class(
  "my_object",
  public=list(
    func=parent_function,
    a=1
  )
)


# Testing
tst <- my_object$new()
tst

## <my_object>
## Public:
## a: 1
## clone: function (deep = FALSE) 
## func: function (x) 


tst$func(8)
tst$a

## [1] 64


tst$func()

## Error in self$a <- 999999 : object 'self' not found

现在的想法是self传递给子函数——因为父函数显然可以看到self,所以它可以传递它

# General function defined outside R6 class
parent_function <- function(x) {
  if (missing(x)) {
    child_function(self)
  } else {
    self$a <- x * x
  }
}

# If interactive, redirect to this function
child_function <- function(self) {
  self$a <- 999999
}

# R6 test object
my_object <- R6::R6Class(
  "my_object",
  public = 
    list(
      func=parent_function,
      a=1
    ),
)

## Testing
tst <- my_object$new()
tst

## <my_object>
## Public:
## a: 1
## clone: function (deep = FALSE) 
## func: function (x) 


tst$func(8)
tst$a

## [1] 64


tst$func()  
tst$a

## [1] 999999