R をオブジェクト指向風に使ってみる
Rでは S4 メソッドでオブジェクト指向は行えるけど、手軽には使いにくい。
そこで、クロージャーと eval を使ってオブジェクト指向風なことをやってみる。
…というのは建前で、s「substitute と eval 使えばクロージャー内の関数呼べそう」と思ったから試してみただけ。
cls <- function(x, y) { show <- function() { cat("x:", x, ", y:", y, "\n") } add_x <- function(v) { x <<- x + v ; exe} add_y <- function(v) { y <<- y + v ; exe} a <- function(...) UseMethod("a") a.default <- function(v) { print("default") } a.matrix <- function(m) { print("matrix") } exe <- function(e){ eval(substitute(e)) } exe } #インスタンス生成 instance <- cls(3, 4) # show() メソッド呼び出し instance(show()) # x の値を返す instance(x) # x の値変更 instance(x <<- 10) # 環境経由の操作 environment(instance)$x environment(instance)$x <- 11 # メソッドを連続して呼び出し instance(add_x(3))(add_y(1))(show()) # 引数の型による多態性 m <- matrix(1:3) instance(a(m)) # a.matrix の呼び出し instance(a(1:3)) # a.default の呼び出し # Ruby の特異メソッド風に inc_x を追加 evalq(inc_x <- function(){x <<- x + 1; exe}, envir=environment(instance)) instance(inc_x())(show()) # サブクラス cls2 <- function(x,y,z) { # スーパークラスの定義をここで再定義 super <- environment(cls(x,y)) for(v in ls(super)) { val <- get(v, super) environment(val) <- environment() assign(v, val) } # サブクラスでの定義 show <- function() { cat("x:", x, ", y:", y, ", z: ", z,"\n") } # スーパークラスの同名のメソッド定義を利用するなら、こんな風にも書ける # show <- (function(){ show<-show; function(){ show(); cat("z: ", z,"\n")} })() add_z <- function(v) { z <<- z + v ; exe} exe } instance2 <- cls2(1,4,3) instance2(show()) instance2(x <<- 10) instance2(add_x(3))(add_y(1))(show())
カプセル化は protect なメソッドぐらいしかできそうにないので(めんどくさいから)やめとく。
サブクラス化の無駄が多い気がするな。再定義の部分どうにかならんかな?
super <- environment(cls(x,y)) e <- environment() parent.env(super) <- parent.env(e) parent.env(e) <- super
とやると、ある程度うまくいくけど、instance2(add_x(3))(show()) の show はスーパークラスの方が呼び出されてしまうし…。
まあ、実際に使うことはないだろうから、どうでもいいんだが。