HIRO: R Advent Calendar 2014
HIRO: R Advent Calendar 2014
R Advent Calendar 2014 14日目の記事です
R6の解説そのまま使わせて頂きました
http://cran.r-project.org/web/packages/R6のVignettes
http://stackoverflow.com/questions/tagged/r6
なぜ今更 R6 Classes?
JapanR 2014で「すごく使いやすい」と聞いたから
S4, R5が使いにくかったから(個人の意見です)
記事にするネタが無くて困っていたから…
HIRO: R Advent Calendar 2014
HIRO: R Advent Calendar 2014
他言語のようなオブジェクト指向
R6パッケージを読み込む
特徴
参照セマンティクス
active bindings
public/private メソッド
継承(スーパークラスとサブクラス)
HIRO: R Advent Calendar 2014
library(R6)
Person <- R6Class("Person",
public = list(
name = NA,
hair = NA,
initialize = function(name, hair) {
if (!missing(name)) self$name <- name
if (!missing(hair)) self$hair <- hair
self$greet()
},
set_hair = function(val) {
self$hair <- val
},
greet = function() {
cat(paste0("Hello, my name is ", self$name, ".¥n"))
}
)
)
Person classを定義する
フィールド
メソッド
コンストラクタ
publicメンバはselfを使って呼び出す
HIRO: R Advent Calendar 2014
ann <- Person$new("Ann", "black")
> Hello, my name is Ann.
ann
> <Person>
> Public:
> greet: function
> hair: black
> initialize: function
> name: Ann
> set_hair: function
Person classのインスタンス化
ann$hair
> black
ann$set_hair <- “red”
#または
ann$hair <- “red”
ann$hair
> red
publicメンバへのアクセス
HIRO: R Advent Calendar 2014
Numbers <- R6Class("Numbers",
public = list(
x = 100
),
active = list(
x2 = function(value) {
if (missing(value)) return(self$x * 2)
else self$x <- value/2
},
rand = function() rnorm(1)
)
)
Number classの定義 publicメンバへのアクセス
n <- Numbers$new()
n$x
> [1] 100
n$rand
#> [1] 0.2648
n$rand
#> [1] 2.171
n$rand <- 3
#> Error: unused argument (quote(3))
active bindingsへのアクセス
active bindings: アクセスされるたびに実行される
HIRO: R Advent Calendar 2014
Queue <- R6Class("Queue",
public = list(
initialize = function(...) {
for (item in list(...)) {
self$push(item)
}
},
push = function(x) {
private$queue <- c(private$queue, list(x))
invisible(self)
},
pop = function() {
if (private$length() == 0) return(NULL)
# Can use private$queue for explicit access
head <- private$queue[[1]]
private$queue <- private$queue[-1]
head
}
),
private = list(
queue = list(),
length = function() base::length(private$queue)
)
) 今度の例はprivateメンバを含むクラス
push(“something”)の度にキューが伸びる
pop()で最初に入ったものから1つずつ取り除かれる
HIRO: R Advent Calendar 2014
q <- Queue$new(5, 6, "foo")
q$push("something")
q$push("another thing")
#q$push("something")$push("another thing"))でも同じ結果
q$pop()
> [1] 5
q$queue
> NULL
q$length()
> Error: attempt to apply non-function
Queue classのインスタンス化
チェーンも可
publicメンバにはアクセス可能
privateメンバにはアクセスできない
HIRO: R Advent Calendar 2014
q <- Queue$new(5, 6, "foo")
q$push("something")
q$push("another thing")
q$push(17)
#q$push("something")$push("another thing")$push(17)
#でも同じ結果
q$pop()
> [1] 5
q$pop()
> [1] 6
Queue classのインスタンス化
HIRO: R Advent Calendar 2014
HistoryQueue <- R6Class("HistoryQueue",
inherit = Queue,
public = list(
show = function() {
cat("Next item is at index", private$head_idx + 1, "¥n")
for (i in seq_along(private$queue)) {
cat(i, ": ", private$queue[[i]], "¥n", sep = "")
}
},
pop = function() {
if (private$length() - private$head_idx == 0) return(NULL)
private$head_idx <<- private$head_idx + 1
private$queue[[private$head_idx]]
}
),
private = list(
head_idx = 0
)
)
Queue classの継承
show() の追加
method() のオーバーライド
HIRO: R Advent Calendar 2014
hq <- HistoryQueue$new(5, 6, "foo")
hq$show()
> Next item is at index 1
> 1: 5
> 2: 6
> 3: foo
hq$pop()
> [1] 5
hq$show()
> Next item is at index 2
> 1: 5
> 2: 6
> 3: foo
hq$pop()
> [1] 6
Queueのサブクラスである HistoryQueueのインスタンス化
HIRO: R Advent Calendar 2014
CountingQueue <- R6Class("CountingQueue",
inherit = Queue,
public = list(
push = function(x) {
private$total <<- private$total + 1
super$push(x)
},
get_total = function() private$total
),
private = list(
total = 0
)
)
cq <- CountingQueue$new("x", "y")
cq$get_total()
> [1] 2
cq$push("z")
cq$pop()
> [1] "x"
cq$pop()
> [1] "y"
cq$get_total()
> [1] 3
Queue classの継承
スーパークラスのメソッドをsuper$で呼び出し
HIRO: R Advent Calendar 2014
SimpleClass <- R6Class("SimpleClass",
public = list(x = NULL)
)
NonSharedField <- R6Class("NonSharedField",
public = list(
e = NULL,
initialize = function() e <<- SimpleClass$new()
)
)
n1 <- NonSharedField$new()
n1$e$x <- 1
n2 <- NonSharedField$new()
n2$e$x <- 2
# n2$e$x does not affect n1$e$x
n1$e$x
> [1] 1
initializeメソッドの中で オブジェクト参照
*initializeメソッドの中ではなく 直接設定される場合、すべてで共有されてしまう(左の例の場合、n1$e$x = 2 となる)
HIRO: R Advent Calendar 2014
NP <- R6Class("NP",
portable = FALSE,
public = list(
x = NA,
getx = function() x,
setx = function(value) x <<- value
)
)
np <- NP$new()
np$setx(10)
np$getx()
> [1] 10
P <- R6Class("P",
portable = TRUE, # This is default
public = list(
x = NA,
getx = function() self$x,
setx = function(value) self$x <- value
)
)
p <- P$new()
p$setx(10)
p$getx()
> [1] 10
non-portable classの場合のみ selfの代わりに <<- 使用可
Portable class: portable=TRUE ・異なるパッケージ間でも継承 ・メンバへのアクセスには、(public) self$x もしくは <<- や private$y が必要
HIRO: R Advent Calendar 2014
clsTrnR6 <- R6Class("clsTrnR6", lock=FALSE, public = list( x = NA, initialize = function(x) { self$x <- x }, push_function = function(name, meth) { self[[name]] <- meth environment(self[[name]]) <- environment(self$push_function) } ) ) clsR6 <- clsTrnR6$new(x=4) clsR6$x #[1] 4 clsR6$push_function("predict", function(y) y*self$x) clsR6$predict(11)
メソッドの追加
HIRO: R Advent Calendar 2014
lock = FALSE
A <- R6Class("A", public = list( initialize = function() { reg.finalizer(self, function(e) print("Finalizer has been called!"),
onexit = TRUE) } )) A$new() 1+1 invisible(gc() > "Finalizer has been called!"
initializeの中で ファイナライザを呼び出し
最後の処理の結果が.Last.valueに入るので 何か別の処理をする
gc()で終了
HIRO: R Advent Calendar 2014
ありがとうございました m(_ _)m
誤訳・誤解等ご指摘ください
あっても大目に見て下さい…
HIRO: R Advent Calendar 2014