15

Rに関して、オブジェクトの継承に関して、Yを含むS4オブジェクトXがある場合、Yにイニシャライザがある場合、Xがイニシャライザの場合、Xのイニシャライザ内からイニシャライザを呼び出す方法を教えてください。構築された。

4

1 に答える 1

23

最初のパス、十分ではありません

ここに2つのクラスがあります

.A <- setClass("A", representation(a="integer"))
.B <- setClass("B", contains="A", representation(b="integer"))

シンボル.Aはクラス ジェネレータ関数 (本質的には への呼び出しnew()) であり、メソッド パッケージへの比較的新しい追加です。

callNextMethodここでは、クラスの次のメソッド (デフォルトのコンストラクター、initialize,ANY-method) を呼び出すために使用する initialize,A-method を記述します。

setMethod("initialize", "A", function(.Object, ..., a=integer()) {
    ## do work of initialization
    cat("A\n")
    callNextMethod(.Object, ..., a=a)
})

関数が名前のない引数を に代入しないように、スロットに対応する引数a=aが後に続きます。名前のない引数は (から) スロットではなく基本クラスの初期化に使用されるため、これは重要です。これの重要性は以下で明らかになります。「B」についても同様です。...a?initialize

setMethod("initialize", "B", function(.Object, ..., b=integer()) {
    cat("B\n")
    callNextMethod(.Object, ..., b=b)
})

そして活動中

> 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

実際、これは正確ではありません。デフォルト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

そして、初期化メソッドは契約のこの側面を破っています

>  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

コピー構築は非常に便利であることが判明したため、これを壊したくありません。

保持コピー構築

コピー作成機能を維持するために採用されている 2 つのソリューションがあります。最初のものは初期化メソッドの定義を回避しますが、代わりに単純な古い関数をコンストラクターとして作成します

.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, ...)
}

これらの関数は...引数としてインクルードされるため、クラス "B1" を拡張し、そのコンストラクターを引き続き使用できます。これは実際には非常に魅力的です。コンストラクターは、文書化された引数を持つ適切な署名を持つことができます。initializeコピー コンストラクターとして使用できます (initialize,A1 メソッドまたは initialize,B1 メソッドがないため、.A1()呼び出しはデフォルトのコピー コンストラクター可能な initialize メソッドを呼び出すことに注意してください)。関数 (.B1(A1(a), b=b, ...)は、「"A1" コンストラクターを使用してそのスーパークラスを作成する名前のない引数と、スロット b に対応する名前付き引数を使用して、クラス B1 のジェネレーターを呼び出す」と述べています。?initialize、名前のない引数は、スーパークラスを初期化するために使用されます(クラス構造に多重継承が含まれる場合、複数のクラスで)。コンストラクターの使用は、クラス A1 と B1 が互いの構造と実装を認識しない可能性があることを意味します。

2 番目の解決策は、それほど一般的ではありませんが、次の行に沿って、コピーの構築を保持する初期化メソッドを記述することです。

.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)
})

引数は、メソッドがコピー コンストラクターとして使用されている場合に関連する、デフォルトとしてのスロットのa1=.Object@a1現在の値を使用します。この例は、 a を使用して長さ 0 のベクトルとは異なる初期値を提供する方法を示しています。実際に:a1.Objectprototype

> 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

残念ながら、基本クラスから派生クラスを初期化しようとすると、このアプローチは失敗します。

その他の考慮事項

最後のポイントは、initialize メソッド自体の構造です。上の図はそのパターンです

## do class initialization steps, then...
callNextMethod(<...>)

初期化callNextMethod()メソッドの最後も同様です。代替案は

.Object <- callNextMethod(<...>)
## do class initialization steps by modifying .Object, e.g.,...
.Object@a <- <...>
.Object

最初のアプローチを好む理由は、コピーの必要性が少ないためです。デフォルトの initialize,ANY-method は最小限のコピーでスロットにデータを取り込みますが、スロット更新アプローチはスロットが変更されるたびにオブジェクト全体をコピーします。オブジェクトに大きなベクトルが含まれている場合、これは非常に悪いことです。

于 2013-04-27T05:41:31.430 に答える