R5クラスの継承の罠
あ...ありのまま 今 起こった事を(ry
R5クラスでサブクラスを定義すると、スーパークラスがインスタンス化されます。
な… 何を言っているのか(ry
本当に意味が分からないです。
setRefClass("Parent", methods=list( initialize = function()print("run"))) setRefClass("Child", contains = "Parent")
Parent
クラスはインスタンス化されるさいに"run"と表示するだけのクラスです。Child
クラスはParent
クラスを継承しているだけのクラスです。
> setRefClass("Parent", + methods=list( + initialize = function()print("run")) + ) > setRefClass("Child", contains = "Parent") [1] "run" [1] "run"
実行するとこうなります。Child
クラスのクラス定義を行っただけで、なぜかParent
クラスが二回インスタンス化されました。
この挙動は、以下のようなクラス定義の場合に問題を引き起こします。
setRefClass("Parent", fields = c("x", "y"), methods = list( initialize = function(...) { initFields(...) y <<- x * 2 }) ) setRefClass("Child", contains = "Parent")
Parent
クラスはインスタンス化されるさいにy <<- x * 2
を実行します。なので、new("Parent", x=1)
のように、インスタンス化のさいに引数でxの値を与えておかなければ、エラーになります。Child
クラスはあいかわらずParent
クラスを継承しているだけのクラスです。
> setRefClass("Parent", + fields = c("x", "y"), + methods = list( + initialize = function(...) { + initFields(...) + y <<- x * 2 + }) + ) > setRefClass("Child", contains = "Parent") 以下にエラー x * 2 : 二項演算子の引数が数値ではありません
Child
クラスはクラス定義すら行えません。
対応策としては、引数に初期値を与えておく、引数が与えられていない場合は即return()
するかなんらかの例外処理を行う、などがあると思います。つまり、引数が与えられていなくても問題が起きないようにする、ということです。
個人的に一番問題だと思うのは、パッケージでR5クラスを使う場合です。パッケージをバイナリにビルドするときやlibrary()
で読み込むときには、パッケージ内のスクリプトがいちど全部実行されるのですが、このときスクリプトのファイルが読み込まれる順番は辞書順です*1。サブクラスの前にスーパークラスが定義されていなければならないというのは、まあスクリプト言語では普通だと思います。しかし、R5クラスの場合、スーパークラスのinitializeメソッド内で使われている関数やクラスが、サブクラスの定義よりも前に定義されている必要がある、という複雑な問題が起きてしまいます。上記の対応策を取っておけば回避はできますが、知らなければ謎のバグに苦しむことになります(なりました)。
この副作用、どこかの文書に載ってるんでしょうか…?
> sessionInfo() R version 3.0.0 (2013-04-03) Platform: x86_64-w64-mingw32/x64 (64-bit) locale: [1] LC_COLLATE=Japanese_Japan.932 LC_CTYPE=Japanese_Japan.932 [3] LC_MONETARY=Japanese_Japan.932 LC_NUMERIC=C [5] LC_TIME=Japanese_Japan.932 attached base packages: [1] stats graphics grDevices utils datasets methods [7] base loaded via a namespace (and not attached): [1] codetools_0.2-8 tools_3.0.0
*1:いろんなパッケージのソースを見てみると、a.R
とかzzz.R
という謎の名前のファイルがありますが、これはつまり最初に読み込んでほしいファイルと最後に読み込んでほしいファイルです。