目前,我正在阅读很多关于软件工程,软件设计,设计模式等的内容.来自完全不同的背景,这对我来说都是新的迷人的东西,所以如果我没有使用正确的技术术语,请耐心等待我描述某些方面;-)
我最终在大部分时间都使用了引用类(R中的OOP方法),因为面向对象似乎是我正在做的很多东西的正确选择.
现在,我想知道是否有人在R中实现MVC(模型视图控制器;也称为MVP:模型视图展示器)模式方面有一些好的建议或经验,最好使用参考类.
我也对其他"标准"设计模式(如观察者,黑板等)的信息非常感兴趣,但我不想让这个问题过于宽泛.我想最酷的事情是看到一些最小的示例代码,但任何指针,"架构",图表或任何其他想法也将非常感谢!
对于那些对类似内容感兴趣的人,我真的可以推荐以下书籍:
更新2012-03-12
我最终想出了一个我对MVC的解释的一个小例子(可能不完全正确;-)).
require("digest")
Run Code Online (Sandbox Code Playgroud)
setRefClass(
"Observer",
fields=list(
.X="environment"
),
methods=list(
notify=function(uid, ...) {
message(paste("Notifying subscribers of model uid: ", uid, sep=""))
temp <- get(uid, .self$.X)
if (length(temp$subscribers)) {
# Call method updateView() for each subscriber reference
sapply(temp$subscribers, function(x) {
x$updateView()
})
}
return(TRUE)
}
)
)
Run Code Online (Sandbox Code Playgroud)
setRefClass(
"Model",
fields=list(
.X="data.frame",
state="character",
uid="character",
observer="Observer"
),
methods=list(
initialize=function(...) {
# Make sure all …
Run Code Online (Sandbox Code Playgroud) 显然,John Chambers 在版本2.12中将R的Reference Classes添加到了R中.网上似乎没有太多的信息,但他们称之为R5课程,这意味着他们与S3和S4课程处于同一水平.
问题:什么是引用类,它如何适应现有的类类型?
我试图在R包中动态生成引用类,并且它被证明是相当困难的.以下是我采取的方法和遇到的问题:
我正在创建一个包,我希望能够在模式中动态读取并自动生成关联的引用类(想想SOAP).当然,这意味着我无法在包源中预先定义我的引用类.
我最初尝试使用简单的方法创建一个新类:
myClass <- setRefClass("NewClassName", fields=list(fieldA="character"))
Run Code Online (Sandbox Code Playgroud)
当然,它在交互式执行时工作正常,但是当包含在包源中时,我会收到locked binding
错误.从我的阅读中看,这看起来是因为当交互式运行时,类信息存储在未锁定的全局环境中,而我的包的基本环境被锁定.
然后我发现了一个建议使用某些东西的线程:
myClass <- setRefClass("NewClassName", fields=list(fieldA="character"), where=globalenv())
Run Code Online (Sandbox Code Playgroud)
当我尝试构建软件包时,这实际上已经崩溃了R/Studio,所以我不知道它生成的错误的日志,但不幸的是,它当然没有用.
接下来我尝试在我的包中创建一个新环境,我可以使用它来存储这些引用类.所以我.classEnv <- new.env()
在我的包源中添加了一行(不在任何函数内部),然后在创建新的引用类时尝试使用该类:
myClass <- setRefClass("NewClassName", fields=list(fieldA="character"), where=.classEnv)
Run Code Online (Sandbox Code Playgroud)
这实际上似乎工作正常,但会产生以下警告:
> myClass <- setRefClass("NewClassName", where=.classEnv)
Warning message:
In getPackageName(where) :
Created a package name, ‘2013-04-23 10:19:14’, when none found
Run Code Online (Sandbox Code Playgroud)
所以,出于某种原因,methods::getPackageName()
我无法拿起我的新环境所在的软件包?
有没有办法以不同的方式创建我的新环境,以便getPackageName()
能够正确识别包?我可以添加一些允许我帮助getPackageName()
检测包的功能吗?如果我可以处理警告,或者我是否通过尝试动态创建它们而滥用引用类,这是否会起作用?
在尝试实现一些优化算法时,我注意到R引用类中有一些奇怪的行为.在特定的初始化方法中似乎存在一些幕后解析魔法,这使得难以使用匿名函数.下面是一个说明难度的示例:我定义了一个优化函数(f_opt),一个对其运行优化的函数,以及一个将这两个作为方法的引用类.奇怪的行为将在代码中更清晰
f_opt <- function(x) (t(x)%*%x)
do_optim_opt <- function(x) optim(x,f)
do_optim2_opt <- function(x)
{
f(x) #Pointless extra evaluation
optim(x,f)
}
optClass <- setRefClass("optClass",methods=list(do_optim=do_optim_opt,
do_optim2=do_optim2_opt,
f=f_opt))
oc <- optClass$new()
oc$do_optim(rep(0,2)) #Doesn't work: Error in function (par) : object 'f' not found
oc$do_optim2(rep(0,2)) #Works.
oc$do_optim(rep(0,2)) #Parsing magic has presumably happened, and now this works too.
Run Code Online (Sandbox Code Playgroud)
它只是我,还是看起来像其他人的错误?
是否可以在R引用类中包含私有成员字段.玩我的一些在线示例:
> Account <- setRefClass( "ref_Account"
> , fields = list(
> number = "character"
> , balance ="numeric")
> , methods = list(
> deposit <- function(amount) {
> if(amount < 0) {
> stop("deposits must be positive")
> }
> balance <<- balance + amount
> }
> , withdraw <- function(amount) {
> if(amount < 0) {
> stop("withdrawls must be positive")
> }
> balance <<- balance - amount
> }
> ) )
>
> …
Run Code Online (Sandbox Code Playgroud) 例如,假设我有以下包调用Test
,我想导出类A
:
# In /R/Test.R:
#' @docType package
#' @import methods
#' @exportClass A
A <- setRefClass("A", methods = list(foo = identity))
Run Code Online (Sandbox Code Playgroud)
但是,在构建和加载后,使用A
的生成器时出现以下错误:
> library(Test)
> A()$foo(1)
Error: could not find function "A"
Run Code Online (Sandbox Code Playgroud)
我检查过我的NAMESPACE
文件内容很好:
exportClasses(A)
import(methods)
Run Code Online (Sandbox Code Playgroud)
出了什么问题?为什么不导出我的类生成器?
是否有可能获得语法
foo$bar(x) <- value
Run Code Online (Sandbox Code Playgroud)
工作在哪里foo
是一个引用类对象,bar
是一个方法?即是可以做"子集分配"并将"替换函数"作为参考类中的方法吗?
是否可以使用其他OO系统获得语法?
示例:
我将用一个用例说明.想象一个引用类,Person
它包含一个人的一些基本信息.特别是,一个叫做的字段fullname
名为list
:
PersonRCGen <- setRefClass("Person",
fields = list(
fullname = "list",
gender = "character"
))
Run Code Online (Sandbox Code Playgroud)
接下来,我们应该定义一些方法来获取和设置fullnames
列表中的特定名称(尝试)给出上面的语法/接口.到目前为止,我最好的尝试是:
PersonRCGen$methods(
name = function(x) { # x is the dataset,
.self$fullname[[x]]
},
`name<-` = function(x, value) {
.self$fullname[[x]] <- value
}
)
Run Code Online (Sandbox Code Playgroud)
这里的命名也应该说明我正在尝试做什么.
我们初始化一个新对象:
a_person <- PersonRCGen$new(fullname = list(first = "Jane", last = "Doe"),
gender = "F")
Run Code Online (Sandbox Code Playgroud)
fullname
直接访问字段并通过定义的get函数访问名字和姓氏按预期工作:
a_person$fullname
#$`first`
#[1] "Jane"
#
#$last
#[1] …
Run Code Online (Sandbox Code Playgroud) 我正在使用一个带有几十个字段的引用类.我已经设置了一个initialize()
采用列表对象的方法.虽然有些字段依赖于列表元素的进一步计算,但大多数字段都是从列表元素直接分配的:
fieldA <<- list$A
fieldB <<- list$B
Run Code Online (Sandbox Code Playgroud)
我当时觉得自动化这个很好.举一个R伪代码的例子(这个例子显然不起作用):
for (field in c('A', 'B', 'C', 'D'))
field <<- list[[field]]
Run Code Online (Sandbox Code Playgroud)
我已经尝试过几次结束运行<<-
,例如:
for field in c('A', 'B', 'C', 'D'))
do.call('<<-' c(field, list[[field]]))
Run Code Online (Sandbox Code Playgroud)
但没有骰子.
我的猜测是,在目前的参考类化身中,这种行为根本不可能,但是认为在SO土地上有人知道有更好的方法可以做到这一点可能是值得的.
不幸的是,像(f+g)(3)
f和g这两个都是一元函数的东西在R中不起作用.因此,我尝试以下列方式重载"+"运算符以获得一元函数:
"+.function" = function(e1, e2){
return(function(x) e1(x) + e2(x))
}
Run Code Online (Sandbox Code Playgroud)
但如果我尝试使用它,这什么都不做.代码
a = function(x) 2*x
(a+a)(2)
Run Code Online (Sandbox Code Playgroud)
产生相同的错误,如果+.function
没有定义.
通过一段时间的游戏,我发现事实上有可能以这种方式添加函数:如果函数是引用类的成员函数,这是有效的!即,以下代码(连同上面的"+"定义)
clsA = setRefClass("clsA",
methods = list(
b = function(x) 2*x
))
inst_a = clsA$new()
(inst_a$b + inst_a$b)(2)
Run Code Online (Sandbox Code Playgroud)
返回"8"(如预期的那样).因此,我已经为我的问题找到了某种解决方法.现在我的问题是:
这种奇怪行为的原因是什么?为什么不+.function
关心"通常"功能而是关注类成员函数?有谁知道如何将操作员"扩展"到通常的功能?
我是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, ...)
{ …
Run Code Online (Sandbox Code Playgroud)