5

S4 Reference Classesインスタンスのフィールドのデフォルト値を定義するにはどうすればよいですか?

通常の S4 クラスの場合、次のprototype引数があります。

setClass("Test_1",
    representation(
        x.1="numeric", 
        x.2="logical", 
        x.3="matrix"
    ),
    prototype=list(
        x.1=10, 
        x.2=FALSE,
        x.3=matrix(0,0,0)
    )
)

> new("Test_1")
An object of class "Test_1"
Slot "x.1":
[1] 10

Slot "x.2":
[1] FALSE

Slot "x.3":
<0 x 0 matrix>

のヘルプページを理解している限り、これは引数setRefClassを介して S4 参照クラスでも機能するはずです。...それぞれのセクションには次のように記載されています。

... setClass に渡されるその他の引数。

まだ正しくprototypeディスパッチされていないようです:setClass

gen <- setRefClass("Test_2",
    fields=list(
        x.1="numeric", 
        x.2="logical", 
        x.3="matrix"
    ),
    prototype=list(
        x.1=10, 
        x.2=FALSE,
        x.3=matrix(0,0,0)
    )
)

> gen$new()
Reference class object of class "Test_2"
Field "x.1":
numeric(0)
Field "x.2":
logical(0)
Field "x.3":
<0 x 0 matrix>

あるいは

> new("Test_2")
Reference class object of class "Test_2"
Field "x.1":
numeric(0)
Field "x.2":
logical(0)
Field "x.3":
<0 x 0 matrix>

のヘルプ ページで、デフォルト値/プロトタイプに関連するものは他に見つかりませんでしたsetRefClass

これはバグですか、それともここで明らかな何かが欠けていますか?


編集

デフォルト値を述べるのに役立つ最も近いものは です$initFields()

これは?setRefClass言わなければならないことです:

指定された引数からオブジェクトのフィールドを初期化します。通常、このメソッドは $initialize() メソッドを持つクラスからのみ呼び出されます。これは、参照クラスのデフォルトの初期化に対応しています。スロットと非参照スーパークラスがある場合、これらも ... 引数で指定できます。

通常、特殊な $initialize() メソッドは独自の計算を実行し、次に $initFields() を呼び出して標準の初期化を実行します。これは、以下の例の matrixViewer クラスに示されています。

ここまでは順調ですね

gen <- setRefClass("Test_3",
    fields=list(
        x.1="numeric", 
        x.2="logical", 
        x.3="matrix"
    ),
    methods=list(
        initialize=function(
            ...
        ) {
            .self$initFields(x.1=10, x.2=TRUE, x.3=matrix(0,0,0), ...)
        }
    )
)

「デフォルトの初期化ケース」の扱いとして機能します。

> gen$new()
Reference class object of class "Test_3"
Field "x.1":
[1] 10
Field "x.2":
[1] TRUE
Field "x.3":
<0 x 0 matrix>

ただし、(一部の) フィールド値が初期化時に明示的に指定されている状況を処理できない場合:

> gen$new(x.1=100)
Reference class object of class "Test_3"
Field "x.1":
[1] 10
Field "x.2":
[1] TRUE
Field "x.3":
<0 x 0 matrix>

回避策

本当に汚いですが、うまくいきます

gen <- setRefClass("Test_4",
    fields=list(
        x.1="numeric", 
        x.2="logical", 
        x.3="matrix"
    ),
    methods=list(
        initialize=function(
            ...
        ) {
            defaults <- list(
                x.1=10,
                x.2=FALSE,
                x.3=matrix(0,0,0)
            )
            if (!missing(...)) {
                x.args      <- list(...)             
                specified   <- names(x.args)
                idx <- which(specified %in% names(defaults))
                if (length(idx)) {
                    for (ii in specified[idx]) {
                        defaults[[ii]] <- x.args[[ii]]
                    }
                }
            }    
            .self$initFields(x.1=defaults$x.1, x.2=defaults$x.2, 
                x.3=defaults$x.3, ...)
        }
    )
)

初期化

> gen$new()
Reference class object of class "Test_4"
Field "x.1":
[1] 10
Field "x.2":
[1] FALSE
Field "x.3":
<0 x 0 matrix>

> gen$new(x.1=100)
Reference class object of class "Test_4"
Field "x.1":
[1] 100
Field "x.2":
[1] FALSE
Field "x.3":
<0 x 0 matrix>
> gen$new(x.1=100)

それが私が探しているものですが、もっと「組み込み」のものがあると確信していますか?


編集2

全体がもう少し一般的です。メソッドensureDefaultValuesは、他の各クラスが継承するクラスのメソッドである可能性があります。「継承パスをさらに下る」クラスの場合、このメソッドはintializeメソッド内で簡単に呼び出すことができます。

gen <- setRefClass("RootClass",
    methods=list(
        ensureDefaultValues=function(values, ...) {
            if (!missing(...)) {
                arguments   <- list(...)             
                specified   <- names(arguments)
                idx <- which(specified %in% names(values))
                if (length(idx)) {
                    for (ii in specified[idx]) {
                        values[[ii]] <- arguments[[ii]]
                    }
                }
            }    
            temp <- paste(paste0(names(values), "=values$", 
                names(values)), collapse=", ")
            eval(parse(text=paste0(".self$initFields(", temp, ", ...)")))
            return(TRUE)            
        }
    )
)

gen <- setRefClass("Test_5",
    contains="RootClass",
    fields=list(
        x.1="numeric", 
        x.2="logical", 
        x.3="matrix"
    ),
    methods=list(
        initialize=function(
            ...
        ) {
            .self$ensureDefaultValues(
                values=list(
                    x.1=10,
                    x.2=FALSE,
                    x.3=matrix(0,0,0)
                ),
                ...
            )
            return(.self)
        }
    )
)
> gen$new()
Reference class object of class "Test_5"
Field "x.1":
[1] 10
Field "x.2":
[1] FALSE
Field "x.3":
<0 x 0 matrix>

> gen$new(x.1=100)
Reference class object of class "Test_5"
Field "x.1":
[1] 100
Field "x.2":
[1] FALSE
Field "x.3":
<0 x 0 matrix>
4

1 に答える 1

10

プロトタイプの概念は、参照クラスに実装されることを意図しているとは思いません。簡単な方法は、 の引数にデフォルトを指定することinitializeです。

Test_1 <- setRefClass("Test_1",
    field = list(x.1="numeric", x.2="logical", x.3="matrix"),
    method = list(initialize =
      function(..., x.1 = 10, x.2 = FALSE, x.3 = matrix(0, 0, 0))
    {
        callSuper(..., x.1 = x.1, x.2 = x.2, x.3 = x.3)
    })
)

次に、ジェネレーター関数 (R-devel で使用可能な構文を使用) を呼び出して、返すことができます。

> Test_1()
Reference class object of class "Test_1"
Field "x.1":
[1] 10
Field "x.2":
[1] FALSE
Field "x.3":
<0 x 0 matrix>
于 2012-11-22T19:12:43.927 に答える