Nic*_*ton 15 inheritance r s4
至于R,可有人向我解释,关于对象继承,如果我有S4对象X,它包含Y,如果Y有一个初始化,这怎么可能初始化从X的初始化中调用,当X是建造.
Mar*_*gan 23
这是两个班级
.A <- setClass("A", representation(a="integer"))
.B <- setClass("B", contains="A", representation(b="integer"))
Run Code Online (Sandbox Code Playgroud)
符号.A是一个类生成器函数(本质上是一个调用new()),是方法包的一个相对较新的补充.
这里我们编写一个初始化的A方法,用于callNextMethod为类调用下一个方法(默认构造函数,初始化,ANY方法)
setMethod("initialize", "A", function(.Object, ..., a=integer()) {
## do work of initialization
cat("A\n")
callNextMethod(.Object, ..., a=a)
})
Run Code Online (Sandbox Code Playgroud)
对应于该槽的参数在此a=a之后,...因此该函数不会将任何未命名的参数分配给a; 这很重要,因为假定(from ?initialize)用于初始化基类,而不是槽; 这一点的重要性在下面变得明显.同样对于"B":
setMethod("initialize", "B", function(.Object, ..., b=integer()) {
cat("B\n")
callNextMethod(.Object, ..., b=b)
})
Run Code Online (Sandbox Code Playgroud)
并在行动
> b <- .B(a=1:5, b=5:1)
B
A
> b
An object of class "B"
Slot "b":
[1] 5 4 3 2 1
Slot "a":
[1] 1 2 3 4 5
Run Code Online (Sandbox Code Playgroud)
实际上,这不太正确,因为默认initialize是复制构造函数
.C <- setClass("C", representation(c1="numeric", c2="numeric"))
c <- .C(c1=1:5, c2=5:1)
> initialize(c, c1=5:1)
An object of class "C"
Slot "c1":
[1] 5 4 3 2 1
Slot "c2":
[1] 5 4 3 2 1
Run Code Online (Sandbox Code Playgroud)
我们的初始化方法打破了合同的这个方面
> initialize(b, a=1:5) # BAD: no copy construction
B
A
An object of class "B"
Slot "b":
integer(0)
Slot "a":
[1] 1 2 3 4 5
Run Code Online (Sandbox Code Playgroud)
复制结构变得非常方便,所以我们不想打破它.
有两种解决方案用于保留复制构造功能.第一个避免定义初始化方法,而是创建一个普通的旧函数作为构造函数
.A1 <- setClass("A1", representation(a="integer"))
.B1 <- setClass("B1", contains="A1", representation(b="integer"))
A1 <- function(a = integer(), ...) {
.A1(a=a, ...)
}
B1 <- function(a=integer(), b=integer(), ...) {
.B1(A1(a), b=b, ...)
}
Run Code Online (Sandbox Code Playgroud)
这些函数包含...作为参数,因此可以扩展类"B1"并仍然使用其构造函数.这实际上非常有吸引力; 构造函数可以有一个带有文档参数的合理签名.initialize可以用作复制构造函数(记住,没有初始化,A1方法或初始化,B1方法,因此调用.A1()调用默认的,复制构造函数初始化方法).该函数(.B1(A1(a), b=b, ...)表示"为类B1调用生成器,使用"A1"构造函数创建其超类的未命名参数,以及与插槽b"对应的命名参数.如上所述?initialize,使用未命名的参数)初始化超类(es)(当类结构涉及多重继承时使用多个类).构造函数的使用意味着类A1和B1可以忽略彼此的结构和实现.
第二种解决方案,不太常用于它的全部荣耀,是编写一个初始化方法,保留复制结构,沿着
.A2 <- setClass("A2", representation(a1="integer", a2="integer"),
prototype=prototype(a1=1:5, a2=5:1))
setMethod("initialize", "A2",
function(.Object, ..., a1=.Object@a1, a2=.Object@a2)
{
callNextMethod(.Object, ..., a1=a1, a2=a2)
})
Run Code Online (Sandbox Code Playgroud)
该参数a1=.Object@a1使用a1slot 的当前值.Object作为默认值,当该方法用作复制构造函数时相关.该示例说明了使用a prototype来提供与0长度向量不同的初始值.在行动:
> a <- .A2(a2=1:3)
> a
An object of class "A1"
Slot "a1":
[1] 1 2 3 4 5
Slot "a2":
[1] 1 2 3
> initialize(a, a1=-(1:3)) # GOOD: copy constructor
An object of class "A1"
Slot "a1":
[1] -1 -2 -3
Slot "a2":
[1] 1 2 3
Run Code Online (Sandbox Code Playgroud)
不幸的是,当尝试从基类初始化派生类时,此方法失败.
最后一点是初始化方法本身的结构.上面说明的是模式
## do class initialization steps, then...
callNextMethod(<...>)
Run Code Online (Sandbox Code Playgroud)
所以callNextMethod()在initialize方法结束时.另一种选择是
.Object <- callNextMethod(<...>)
## do class initialization steps by modifying .Object, e.g.,...
.Object@a <- <...>
.Object
Run Code Online (Sandbox Code Playgroud)
偏好第一种方法的原因是涉及的复制较少; 默认初始化,ANY方法以最少的复制填充插槽,而插槽更新方法每次修改插槽时复制整个对象; 如果对象包含大向量,这可能非常糟糕.