Rap*_*ter 8 oop r pass-by-reference s4 reference-class
我是S4参考类的忠实粉丝,因为它们允许混合编程风格(功能/按值传递与oop/pass-by-reference; 示例),从而大大提高了灵活性.
但是,当我要求它通过方法检索某个字段值时,我认为我刚刚遇到了关于R扫描环境/帧的方式的不良行为$field()(请参阅帮助页面).问题是,如果在实际的本地/目标环境中找不到所需的字段(这将构成S4参考类的环境),R似乎也会查看封闭的环境/框架,即它就像运行一样get(<objname>, inherits=TRUE)(请参阅帮助)页面).
为了具有R只是看在本地/目标的环境,我的想法是这样$field(name="<fieldname>", inherits=FALSE),但$field()没有一个...说法,让我通过inherits=FALSE一起get()(我猜是沿途某处调用).这有解决方法吗?
对于那些对更多细节感兴趣的人:这是一个说明行为的小代码示例
setRefClass("A", fields=list(a="character"))
x <- getRefClass("A")$new(a="a")
Run Code Online (Sandbox Code Playgroud)
有一个领域a类A,所以它在目标环境中找到,返回的值:
> x$field("a")
[1] "a"
Run Code Online (Sandbox Code Playgroud)
如果我们尝试访问不是引用类的字段但恰好具有与工作空间/搜索路径中的某个其他对象的名称相同的字段(在本例中"lm"),则情况会有所不同:
require("MASS")
> x$field("lm")
function (formula, data, subset, weights, na.action, method = "qr",
model = TRUE, x = FALSE, y = FALSE, qr = TRUE, singular.ok = TRUE,
contrasts = NULL, offset, ...)
{
ret.x <- x
ret.y <- y
[omitted]
if (!qr)
z$qr <- NULL
z
}
<bytecode: 0x02e6b654>
<environment: namespace:stats>
Run Code Online (Sandbox Code Playgroud)
这不是我期望的.恕我直言错误或至少警告会好得多.或者打开$field()可以通过其他函数传递的参数的方法....我猜测在get()调用时会调用某个地方$field(),所以这样的事情可能会阻止上述行为的发生:
x$field("digest", inherits=FALSE)
Run Code Online (Sandbox Code Playgroud)
这应该可以解决问题,但也许有一些更优雅的东西不涉及新方法的规范$field():
setRefClass("A", fields=list(a="character"),
methods=list(
myField=function(name, ...) {
# VALIDATE NAME //
if (!name %in% names(getRefClass(class(.self))$fields())) {
stop(paste0("Invalid field name: '", name, "'"))
}
# //
.self$field(name=name)
}
)
)
x <- getRefClass("A")$new(a="a")
> x$myField("a")
[1] "a"
> x$myField("lm")
Error in x$myField("lm") : Invalid field name: 'lm'
Run Code Online (Sandbox Code Playgroud)
默认field()方法可以替换为您自己的方法。因此,添加一个inherits参数来避免封闭框架只需获取现有x$field定义并添加它......
setRefClass( Class="B",
fields= list( a="character" ),
methods= list(
field = function(name, value, inherits=TRUE ) {
if( missing(value) ) {
get( name, envir=.self, inherits=inherits )
} else {
if( is.na( match( name, names( .refClassDef@fieldClasses ) ) ) ) {
stop(gettextf("%s is not a field in this class", sQuote(name)), domain = NA)
}
assign(name, value, envir = .self)
}
}
),
)
Run Code Online (Sandbox Code Playgroud)
或者你可以通过一些重新排列得到一个很好的错误消息
setRefClass( Class="C",
fields= list( a="character" ),
methods= list(
field = function(name, value, inherits=TRUE ) {
if( is.na( match( name, names( .refClassDef@fieldClasses ) ) ) &&
( !missing(value) || inherits==FALSE) ) {
stop(gettextf("%s is not a field in this class", sQuote(name)), domain = NA)
}
if( missing(value) ) {
get( name, envir=.self, inherits=inherits )
} else {
assign(name, value, envir = .self)
}
}
),
)
Run Code Online (Sandbox Code Playgroud)
由于您可以定义任何自己的方法来替换默认值,因此几乎可以为引用类实现您想要的任何逻辑。如果使用继承获取变量但模式与 c("expression", "name", "symbol", "function") 匹配,则可能会出现错误,并且如果它与本地 refClass 字段名称不直接匹配,则会发出警告?