R5クラスを利用してクラスを実装する

Rでクラスを実装するには3つの方法がある。

  • S3オブジェクト
  • S4オブジェクト
  • R5オブジェクト

世に出回っている多くのクラスはS3で実装されているようだが(自分の見た限りだと)、S3は「インスタンス生成時に型エラーを出さない」という致命的な欠陥がある。(と、いうより設計思想だけど)

なので、S3は「負の遺産」だとぼくは思っていて、型チェックのあるS4かR5に切り替えるべきだとぼくは思う。

S4は本格的な「オブジェクト指向の〜」とか説明されているが、どこらへんが「本格的なオブジェクト指向」なのかいまいちわからない。

よっぽどR5オブジェクトの方がオブジェクト指向に近い気がする。(基本的な概念は Javaと同じように考えてもよさそう)

そんなわけでR5クラスの練習をしてみた。

R5クラス定義の記法

みんなのPython p244にある、「体積を求める」クラスをR5で書いてみた。 

R5クラスを定義するには

クラス名 <- setRefClass(
    Class = "クラス名",
    fields = list(クラス変数名 = "型"),
    methods = list(
        function{
            メソッドの処理内容
        }
    )
)

の記法で Classとfieldsとmethodsを定義する。

ここで注意なのが、<-は使ってはダメっぽいってところ。

Class <- "クラス名",
fields <- list(),
methods <- list()

とか記述しちゃうとR特有のFuxxking Shxxtなエラーが発生するので、要注意

以下にエラー FUN(X[[1L]], ...) : 
'contains' 引数は上位クラスの名前であるべきです: クラス “function” の要素を得ました

実際に記述してみる。

Prism <- setRefClass(
  Class = "Prism",

  fields = list(
    width = "numeric", # 左辺に型を記述する
    height = "numeric",
    depth = "numeric",
    v = "numeric"
  ),
  
  methods = list(
    CalcContent = function(){
      v <<- width * height * depth # アトリビュートへの代入は二重の演算子を使う
    },
    PrintResult = function(){
      print(v)
    }
  ) 
)

インスタンスの生成

content_instance1 <- Prism$new()  # new methodでインスタンスの生成ができるけど・・・このやり方は非推奨らしい
content_instance1$depth <- "hoge"  # 一応、宣言した型と違うものを代入したら、型エラーが出る
## Error: invalid assignment for reference class field 'depth', should be
## from class "numeric" or a subclass (was class "character")
content_instance2 <- Prism$new(width = 10, height = 20, depth = 30)  # この記法の方が推奨される
content_instance3 <- Prism$new(width = 10, height = 20, depth = "hoge")  # この場合でも型エラーがでる
## Error: invalid assignment for reference class field 'depth', should be
## from class "numeric" or a subclass (was class "character")

クラスメソッドの呼び出し

content_instance2$CalcContent()
content_instance2$PrintResult()
## [1] 6000

参考 ここ