hoge_math's diary

SIerでデータマイニングの仕事をしています。機械学習、データマイニング、数学、プログラミング(R, Python)のことなどを書きます。

RのR5クラス(レファレンスクラス)の注意点

Rでオブジェクト指向を実現する方法の一つにR5という機能がある。オブジェクトの参照わたしが可能になる。

先日、仕事でR5機能を使ってある機械学習アルゴリズムを実装する機会があったので、そのときいろいろ詰まったことをまとめておく。

R5クラスについて簡単におさらいしておく。 R5クラスは以下のように定義できる。

fuga <- setRefClass(
  #オブジェクトの名前
  "fuga",
  
  #メンバ変数
  fields = list(
    hoge = "numeric"
    ),
  
  #メソッド
  methods = list(
    
    #イニシャライザ
    initialize = function(x) {
      hoge <<-x #メンバ変数には<<-でアクセスする
    },
    
    myprint = function() {
      print(hoge)
    }
    )
  )

変数やメソッドには$でアクセスできる。 インスタンスの生成にはnewメソッドを用いる。newメソッドの動作はinitializeで記述する。

> x <- fuga$new(2)
> x$myprint()
[1] 2

さて、問題はここから。R5クラスのメンバ変数の型にはR5クラスを指定することも可能だが、、

popo <- setRefClass(
  "popo",
  fields = list(v = "fuga")
)

しかし、これを実行すると次のようにエラーが出てしまう。

> popo <- setRefClass(
+     "popo",
+     fields = list(v = "fuga")
+ )
 以下にエラー .Object$initialize(...) : 
   引数 "x" がありませんし、省略時既定値もありません 

いろいろ調べてみると、これはpopoの定義の際、変数であるfugaのイニシャライザが呼ばれているのが原因らしい。つまり、引数なしでfuga$initializeが呼ばれてエラーが出ているようだ。そこで、ためしにfugaをのイニシャライザを次のように変更してみる。R5クラスは$methodsでメソッドを書き換えることができる。

fuga$methods(
  initialize = function(x=5) {
    hoge <<-x
    print(hoge)
  }
)

ふたたびpopoを定義すると、fugaのイニシャライザが呼ばれ5がプリントされているのが分かる。

> popo <- setRefClass(
+     "popo",
+     fields = list(v = "fuga")
+ )
[1] 5

また、R5クラスの継承を行っても同じ現象が起こる。

R5クラスの継承の罠 - はやしのブログ Rev.3

この記事にもあるように、対処法としてはメソッドの引数に初期値を与えておくことなどが考えられる。

うーん。。これはかなり致命的なバグな気がするのだが、仕様なのだろうか。。