R中包含自定义字段类的引用类?

Mat*_*ert 5 oop r s4 reference-class

我想在另一个引用类中使用自定义引用类,但此代码失败:

nameClass <- setRefClass("nameClass", fields = list(first = "character",
                                                last = "character"),
                     methods = list(
                       initialize = function(char){
                         chunks <- strsplit(char,"\\.")
                         first <<- chunks[[1]][1]
                         last <<- chunks[[1]][2]
                       },
                       show = function(){
                         cat("Special Name Class \n:")
                         cat("First Name:")
                         methods::show(first)
                         cat("Last Name:")
                         methods::show(last)
                       }
                       ))
# this works fine
nameClass$new("tyler.durden")
Run Code Online (Sandbox Code Playgroud)

当我尝试添加具有类字段的第二个类时,nameClass无法启动此类.

personClass <- setRefClass("personClass", fields = list(fullname = "nameClass",
                                                    occupation = "character"),
                       methods = list(
                         initialize = function(Obj){
                           nm  <- deparse(substitute(Obj))
                           fullname <<- nameClass$new(nm)
                           occupation <<- Obj
                         }))
Run Code Online (Sandbox Code Playgroud)

这只是回报:

 Error in strsplit(char, "\\.") : 
 argument "char" is missing, with no default
Run Code Online (Sandbox Code Playgroud)

我可以想象一个解决方案,其中nameClass是一个S4类,但我读了一点让我有点害怕混合S4和参考类.我是否缺少某些东西,或者当我想要更准确地定义这个特定的名称字段而不仅仅是'字符'时,我应该简单地使用S4类?

我也发现这个帖子有一个很有希望的标题,但无法弄清楚这可以解决我的问题.

Mar*_*gan 10

这是S4系统中常见问题的变体,其中继承new使用零参数进行调用必须起作用.这是因为实现了继承的方式,其中实例化基类,然后使用派生类中的值填充.要实例化基类,需要在没有任何参数的情况下创建它.你有一个问题用图说明

> nameClass()
Error in .Internal(strsplit(x, as.character(split), fixed, perl, useBytes)) : 
  'x' is missing
Run Code Online (Sandbox Code Playgroud)

解决方案是在initialize方法中提供默认参数

initialize=function(char=charcter()) { <...> }
Run Code Online (Sandbox Code Playgroud)

或以其他方式安排(例如,通过missing(char)在体内进行测试initialize)对构造函数进行无论证调用.

编程最佳实践可能会规定initialize方法接受一个...参数并且callSuper()在其体内,因此派生类可以利用基类(例如,字段分配)功能.为了避免无意中匹配未命名的参数的问题,我认为签名应该最终围绕一个模板看起来像

initialize(..., char=character()) { callSuper(...) }
Run Code Online (Sandbox Code Playgroud)

该方案依赖于"空"的合适定义nameClass.以下可能有太多的意见和观点的变化立即有用,但是... nameClass在数据框架中将其视为"行" 是很诱人的,但它更好(因为R在矢量上效果最好)才能想到它作为描述列.考虑到这一点,'empty'nameClass的合理表示是firstlast,每个字段的长度为0.然后

nameClass <- setRefClass("nameClass",
    fields = list(first = "character", last = "character"),
    methods = list(
      initialize = function(..., char=character()){
          if (length(char)) {
              names <- strsplit(char, ".", fixed=TRUE)
              .first <- vapply(names, "[[", character(1), 1)
              .last <- vapply(names, "[[", character(1), 2)
          } else {
              .first <- character()
              .last <- character()
          }
          callSuper(..., first=.first, last=.last)
      }, show = function(){
          .helper <- function(x)
              sprintf("%s%s", paste(sQuote(head(x)), collapse=", "),
                      if (length(x) > 6) ", ..." else "")
          cat("Special Name Class (n = ", length(first), ")\n", sep="")
          cat("First names:", .helper(first), "\n")
          cat("Last names:", .helper(last), "\n")
      }))
Run Code Online (Sandbox Code Playgroud)

与测试用例一样

> nameClass()
Special Name Class (n = 0)
First names:  
Last names:  
> nameClass(char="Paul.Simon")
Special Name Class (n = 1)
First names: 'Paul' 
Last names: 'Simon' 
> nameClass(char=c("Paul.Simon", "Frank.Sinatra"))
Special Name Class (n = 2)
First names: 'Paul', 'Frank' 
Last names: 'Simon', 'Sinatra' 
> nameClass(char=paste(LETTERS, letters, sep="."))
Special Name Class (n = 26)
First names: 'A', 'B', 'C', 'D', 'E', 'F', ... 
Last names: 'a', 'b', 'c', 'd', 'e', 'f', ... 
Run Code Online (Sandbox Code Playgroud)

派生类可以定义为

personClass <- setRefClass("personClass",
    fields = list(fullname = "nameClass", occupation = "character"),
    methods = list(
      initialize = function(..., fullname=nameClass(),
                            occupation=character()) {
          callSuper(..., fullname=fullname, occupation=occupation)
      }))
Run Code Online (Sandbox Code Playgroud)

与测试用例一样

personClass()
personClass(fullname=nameClass())
personClass(fullname=nameClass(), occupation=character())
personClass(fullname=nameClass(char="some.one"), occupation="job")
Run Code Online (Sandbox Code Playgroud)