【问题标题】:Reference Class with custom field classes in R?R中带有自定义字段类的参考类?
【发布时间】:2013-09-19 11:23:29
【问题描述】:

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

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")

当我尝试添加具有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
                         }))

这只是返回:

 Error in strsplit(char, "\\.") : 
 argument "char" is missing, with no default

我可以想象一个解决方案,其中 nameClass 是一个 S4 类,但我读了一点,让我有点害怕混合 S4 和参考类。当我想更准确地定义这个特定的名称字段而不仅仅是“字符”时,我是否遗漏了什么或者我应该简单地使用 S4 类?

我还发现 this thread 的标题很有希望,但不知道这如何解决我的问题。

【问题讨论】:

    标签: r oop s4 reference-class


    【解决方案1】:

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

    说明你有问题
    > nameClass()
    Error in .Internal(strsplit(x, as.character(split), fixed, perl, useBytes)) : 
      'x' is missing
    

    解决方案是在你的初始化方法中提供一个默认参数

    initialize=function(char=charcter()) { <...> }
    

    或以其他方式安排(例如,通过在 initialize 的主体中测试 missing(char))对构造函数进行无参数调用。

    可能的编程最佳实践将规定初始化方法采用... 参数并在其主体中包含callSuper(),以便派生类可以利用基类(例如,字段分配)功能。为了避免无意匹配未命名参数的问题,我认为签名可能最终应该围绕一个看起来像

    的模板构建
    initialize(..., char=character()) { callSuper(...) }
    

    此方案依赖于“空”nameClass 的适当定义。以下内容可能有太多的观点和观点的改变,以至于无法立即使用,但是......很容易将nameClass 视为数据框中的“行”,但最好(因为 R 在向量上效果最好)将其视为描述列。考虑到这一点,“空”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")
          }))
    

    带有测试用例,例如

    > 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', ... 
    

    派生类可能被定义为

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

    带有测试用例,例如

    personClass()
    personClass(fullname=nameClass())
    personClass(fullname=nameClass(), occupation=character())
    personClass(fullname=nameClass(char="some.one"), occupation="job")
    

    【讨论】:

    • 妈的,你打我,我应该刷新页面!
    • 谢谢马丁,这对我来说很有意义,直到最后一段之前的右边:要么提供默认参数,要么通过丢失截取空字符串。但是你能详细说明一下那个 callSuper()。在这里叫什么超级?
    • 或者换个说法。我想使用类的原因是因为我想限制创建的内容。否则我只能使用字符。假设我不想满足一些length(strsplit(somename,"\\.")[[1]]) == 2。使用像 character() 这样的默认参数,我基本上可以输入所有内容。我该如何处理?例如:如果一个人没有专有名称,则不应初始化该对象。
    【解决方案2】:

    这似乎是因为您的“nameClass”没有默认构造函数:

    nameClass$new()
    Error in strsplit(char, "\\.") : 
      argument "char" is missing, with no default
    

    如果你像这样修改你的 nameClass:

    nameClass <- setRefClass("nameClass", fields = list(first = "character",
                                                        last = "character"),
                             methods = list(
                               initialize = function(s = NULL) {
                                 if (!is.null(s) && nzchar(s)) {
                                   chunks <- strsplit(s,"\\.")
                                   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)
                               }
                             ))
    

    然后:

    nameClass$new()
    Special Name Class 
    :First Name:character(0)
    Last Name:character(0)
    

    你的 personClass 现在可以正常工作了(但初始化方法很奇怪):

    personClass$new("tyler.durden")
    Reference class object of class "personClass"
    Field "fullname":
    Special Name Class 
    :First Name:[1] "\"tyler"
    Last Name:[1] "durden\""
    Field "occupation":
    [1] "tyler.durden"
    

    【讨论】:

      猜你喜欢
      • 2012-10-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-01-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多