我想在另一个引用类中使用自定义引用类,但此代码失败:
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有一个很有希望的标题,但无法弄清楚这是如何解决我的问题的。
答案 0 :(得分:10)
这是S4系统中常见问题的变体,其中继承以零参数调用new
必须起作用。这是因为实现了继承的方式,其中实例化基类,然后使用派生类中的值填充。要实例化基类,需要在没有任何参数的情况下创建它。
> nameClass()
Error in .Internal(strsplit(x, as.character(split), fixed, perl, useBytes)) :
'x' is missing
,解决方案是在初始化方法
中提供默认参数initialize=function(char=charcter()) { <...> }
或以其他方式安排(例如,通过测试missing(char)
正文中的initialize
),以便对构造函数进行无参数调用。
可能编程最佳实践将指示initialize方法采用...
参数并且在其主体中具有callSuper()
,以便派生类可以利用基类(例如,字段分配)功能。为了避免无意中匹配未命名的参数的问题,我认为签名应该最终围绕一个看起来像
initialize(..., char=character()) { callSuper(...) }
此方案依赖于“空”nameClass
的合适定义。以下可能有太多的意见和观点的变化,以立即有用,但......很容易将nameClass
视为数据框中的“行”,但它更好(因为R在向量上效果最好) )将其视为描述列。考虑到这一点,“空”nameClass的合理表示是first
和last
字段各自的长度为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")
答案 1 :(得分:1)
这似乎是因为你没有“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"