为S4 Reference Classes的实例定义默认字段值

Rap*_*ter 5 prototype default r s4 reference-class

如何定义S4 Reference Classes实例的字段默认值?

对于常规S4类,有以下prototype论点:

setClass("Test_1",
    representation(
        x.1="numeric", 
        x.2="logical", 
        x.3="matrix"
    ),
    prototype=list(
        x.1=10, 
        x.2=FALSE,
        x.3=matrix(0,0,0)
    )
)

> new("Test_1")
An object of class "Test_1"
Slot "x.1":
[1] 10

Slot "x.2":
[1] FALSE

Slot "x.3":
<0 x 0 matrix>
Run Code Online (Sandbox Code Playgroud)

据我所知,帮助页面setRefClass,这也适用于S4参考类通过...参数.各节说:

...要传递给setClass的其他参数.

prototype似乎没有setClass正确派遣:

gen <- setRefClass("Test_2",
    fields=list(
        x.1="numeric", 
        x.2="logical", 
        x.3="matrix"
    ),
    prototype=list(
        x.1=10, 
        x.2=FALSE,
        x.3=matrix(0,0,0)
    )
)

> gen$new()
Reference class object of class "Test_2"
Field "x.1":
numeric(0)
Field "x.2":
logical(0)
Field "x.3":
<0 x 0 matrix>
Run Code Online (Sandbox Code Playgroud)

另外

> new("Test_2")
Reference class object of class "Test_2"
Field "x.1":
numeric(0)
Field "x.2":
logical(0)
Field "x.3":
<0 x 0 matrix>
Run Code Online (Sandbox Code Playgroud)

我没有在帮助页面上找到与默认值/原型相关的任何其他内容setRefClass.

这是一个错误还是我错过了一些明显的东西?


编辑

我能找到的最接近我的默认值是$initFields().

?setRefClass就是说:

从提供的参数初始化对象的字段.通常只从具有$ initialize()方法的类调用此方法.它对应于引用类的默认初始化.如果有插槽和非参考超类,那么也可以在...参数中提供这些超类.

通常,专门的$ initialize()方法执行自己的计算,然后调用$ initFields()来执行标准初始化,如下例中的matrixViewer类所示.

到现在为止还挺好

gen <- setRefClass("Test_3",
    fields=list(
        x.1="numeric", 
        x.2="logical", 
        x.3="matrix"
    ),
    methods=list(
        initialize=function(
            ...
        ) {
            .self$initFields(x.1=10, x.2=TRUE, x.3=matrix(0,0,0), ...)
        }
    )
)
Run Code Online (Sandbox Code Playgroud)

适用于"默认初始化案例":

> gen$new()
Reference class object of class "Test_3"
Field "x.1":
[1] 10
Field "x.2":
[1] TRUE
Field "x.3":
<0 x 0 matrix>
Run Code Online (Sandbox Code Playgroud)

但是如果无法处理在初始化时显式指定(某些)字段值的情况:

> gen$new(x.1=100)
Reference class object of class "Test_3"
Field "x.1":
[1] 10
Field "x.2":
[1] TRUE
Field "x.3":
<0 x 0 matrix>
Run Code Online (Sandbox Code Playgroud)

解决方法

真的很脏,但它的确有效

gen <- setRefClass("Test_4",
    fields=list(
        x.1="numeric", 
        x.2="logical", 
        x.3="matrix"
    ),
    methods=list(
        initialize=function(
            ...
        ) {
            defaults <- list(
                x.1=10,
                x.2=FALSE,
                x.3=matrix(0,0,0)
            )
            if (!missing(...)) {
                x.args      <- list(...)             
                specified   <- names(x.args)
                idx <- which(specified %in% names(defaults))
                if (length(idx)) {
                    for (ii in specified[idx]) {
                        defaults[[ii]] <- x.args[[ii]]
                    }
                }
            }    
            .self$initFields(x.1=defaults$x.1, x.2=defaults$x.2, 
                x.3=defaults$x.3, ...)
        }
    )
)
Run Code Online (Sandbox Code Playgroud)

Intialization

> gen$new()
Reference class object of class "Test_4"
Field "x.1":
[1] 10
Field "x.2":
[1] FALSE
Field "x.3":
<0 x 0 matrix>

> gen$new(x.1=100)
Reference class object of class "Test_4"
Field "x.1":
[1] 100
Field "x.2":
[1] FALSE
Field "x.3":
<0 x 0 matrix>
> gen$new(x.1=100)
Run Code Online (Sandbox Code Playgroud)

这就是我在寻找的东西,但我确信还有更"内置"的东西?


编辑2

整个事情更通用一点.方法ensureDefaultValues可以是每个其他类继承的类的方法.对于"继承路径下方"的类,可以在intialize方法中简单地调用此方法:

gen <- setRefClass("RootClass",
    methods=list(
        ensureDefaultValues=function(values, ...) {
            if (!missing(...)) {
                arguments   <- list(...)             
                specified   <- names(arguments)
                idx <- which(specified %in% names(values))
                if (length(idx)) {
                    for (ii in specified[idx]) {
                        values[[ii]] <- arguments[[ii]]
                    }
                }
            }    
            temp <- paste(paste0(names(values), "=values$", 
                names(values)), collapse=", ")
            eval(parse(text=paste0(".self$initFields(", temp, ", ...)")))
            return(TRUE)            
        }
    )
)

gen <- setRefClass("Test_5",
    contains="RootClass",
    fields=list(
        x.1="numeric", 
        x.2="logical", 
        x.3="matrix"
    ),
    methods=list(
        initialize=function(
            ...
        ) {
            .self$ensureDefaultValues(
                values=list(
                    x.1=10,
                    x.2=FALSE,
                    x.3=matrix(0,0,0)
                ),
                ...
            )
            return(.self)
        }
    )
)
> gen$new()
Reference class object of class "Test_5"
Field "x.1":
[1] 10
Field "x.2":
[1] FALSE
Field "x.3":
<0 x 0 matrix>

> gen$new(x.1=100)
Reference class object of class "Test_5"
Field "x.1":
[1] 100
Field "x.2":
[1] FALSE
Field "x.3":
<0 x 0 matrix>
Run Code Online (Sandbox Code Playgroud)

Mar*_*gan 10

我不认为原型的概念是为了参考类而实现的.一个简单的方法是在参数中提供默认值initialize,as

Test_1 <- setRefClass("Test_1",
    field = list(x.1="numeric", x.2="logical", x.3="matrix"),
    method = list(initialize =
      function(..., x.1 = 10, x.2 = FALSE, x.3 = matrix(0, 0, 0))
    {
        callSuper(..., x.1 = x.1, x.2 = x.2, x.3 = x.3)
    })
)
Run Code Online (Sandbox Code Playgroud)

然后可以调用生成器函数(使用R-devel中可用的语法)返回

> Test_1()
Reference class object of class "Test_1"
Field "x.1":
[1] 10
Field "x.2":
[1] FALSE
Field "x.3":
<0 x 0 matrix>
Run Code Online (Sandbox Code Playgroud)