More Related Content
Similar to R6パッケージの紹介―機能と実装 (20)
R6パッケージの紹介―機能と実装
- 2. • R6パッケージとは
• R6を使う
– 既存のオブジェクト指向システム(S3, S4, RC)
– R6の機能
• R6の中身をのぞいてみる
– Rにおける環境
– R6Class()の実装
– $new()の実装
• まとめ
1
- 3. • R6パッケージとは
• R6を使う
– 既存のオブジェクト指向システム(S3, S4, RC)
– R6の機能
• R6の中身をのぞいてみる
– Rにおける環境
– R6Class()の実装
– $new()の実装
• まとめ
2
- 5. R6パッケージとは
• 既に3つもOOシステムがあるのに,なぜまたもう1つ?
– S3, S4, RC (Reference Class)
• R6の特徴
– より「普通の」オブジェクト指向っぽい
• 「普通の」= Java, C++, Python, etc.
• RCと同様のreference semantics
• フィールド,メソッドにpublic / private が指定可能
• パッケージをまたいだ継承が可能
– RCより速くて軽い
– 設計・実装が単純
• Rの基本的な機能だけを使って実装されている
4
- 6. • R6パッケージとは
• R6を使う
– 既存のオブジェクト指向システム(S3, S4, RC)
– R6の機能
• R6の中身をのぞいてみる
– Rにおける環境
– R6Class()の実装
– $new()の実装
• まとめ
5
- 7. 6
既存のOOシステム
• S3
– クラスはclass属性として個別のオブジェクトに対して設定
• フォーマルなクラス定義はない
• 継承もclass属性で行う
– メソッドはジェネリック関数
john <- list(name = "John", age = 40)
class(john) <- c("Employee", "Person")
# printはジェネリック関数
print
#> function (x, ...)
#> UseMethod("print")
# メソッド定義
print.Person <- function(x) {
paste0("こんにちは,", x$name, "です.")
}
# printで実際に呼ばれるのはprint.Person
print(john)
#> [1] "こんにちは,Johnです."
- 8. 7
既存のOOシステム
• S4
– S3よりフォーマルで厳密なクラス定義
– スロット(フィールド)には@でアクセス
# クラス定義
setClass("Person",
slots = list(name = "character", age = "numeric"))
# containsで継承
setClass("Employee", contains = "Person"
slots = list(boss = "Person"))
# new()でオブジェクト作成
alice <- new("Person", name = "Alice", age = 40)
john <- new("Employee", name = "John", age = 20, boss = alice)
# @でスロットにアクセス
alice@age
#> [1] 40
# クラス定義にないスロットは作れない
alice@age <- '40'
#> Error in checkAtAssignment("Person", "age", "character") : ...
alice@sex <- "female"
#> Error in checkAtAssignment("Person", "sex", "character") : ...
- 9. 8
既存のOOシステム
• S4
– メソッドはジェネリック関数
– copy-on-modify semantics: オブジェクトは変更不能(immutable)
# ジェネリック関数
setGeneric("greet", function(x) {
standardGeneric("greet")
})
# メソッドの実装
setMethod("greet",
signature = c(x = "Person"),
definition = function(x) {
paste0("こんにちは,", x@name, "です.")
})
greet(john)
#> [1] "こんにちは,Johnです."
# aliceとalice2は別人
alice2 <- alice
alice2@age <- 50
alice2@age
#> [1] 50
alice@age
#> [1] 40
age=50
name=“alice”
age=40
name=“alice”
Person alice
Person alice2
copy on modify
- 10. 9
既存のOOシステム
• RC (Reference Class)
– メソッドはオブジェクトに属する
– フィールドとメソッドには$でアクセス
# クラス定義
Person <- setRefClass("Person",
# フィールド定義
fields = list(name = “character”,
age = "numeric", hair = "character"),
# メソッド定義
methods = list(
# オブジェクトの初期化関数
# フィールドへの代入は<<-か.selfを使う
initialize = function(name, age, hair) {
name <<- name
.self$age <- age
.self$hair <- hair
},
set_hair = function(hair) {
.self$hair <- hair
},
greet = function() {
paste0("こんにちは,", name, "です.")
})
)
# オブジェクト作成
alice <- Person$new("Alice", 40, "red")
# $でメソッドアクセス
alice$greet()
# 継承はcontains引数で
Employee <- setRefClass("Employee",
contains = "Person",
methods = list(
# メソッドオーバーライド
greet = function() {
paste0("こんにちは,会社員の",
name, "です.")
})
)
john <- Employee$new("John", 25, "black")
john$greet()
#> [1] "こんにちは,会社員のJohnです."
- 11. 10
既存のOOシステム
• RC (Reference Class)
– reference semantics: オブジェクトは変更可能(mutable)
– 「普通の」OOP言語っぽい
name = “john”
age = 25
hair = “green”
Employee john
Employee john2
john$hair
#> [1] "black"
# johnとjohn2は同じ人
john2 <- john
john2$set_hair("green")
john$hair
#> [1] "green“
- 12. • R6パッケージとは
• R6を使う
– 既存のオブジェクト指向システム(S3, S4, RC)
– R6の機能
• R6の中身をのぞいてみる
– Rにおける環境
– R6Class()の実装
– $new()の実装
• まとめ
11
- 13. 12
R6の機能
• クラス定義
– syntaxはRCに類似しているが,より簡潔
– フィールドとメソッドを分けずに全てpublic引数に記述すればよい
– RCと異なり,フィールドの型は指定できない
# クラス定義
Person <- R6Class("Person",
# publicでフィールドとメソッドを両方設定する
public = list(
# RCと異なり,変数の型ではなくデフォルト値を設定
name = NA,
hair = "black",
# オブジェクトの初期化関数
# オブジェクト自身にはselfでアクセス
initialize = function(name, hair) {
self$name <- name
self$hair <- hair
},
set_hair = function(hair) {
self$hair <- hair
},
greet = function() {
paste0("こんにちは,", self$name, "です.")
})
)
- 14. 13
R6の機能
• オブジェクト作成
– $new()メソッドで作成
– reference semantics
# オブジェクト作成
alice <- Person$new("Alice", “black")
# フィールド,メソッドには$でアクセス
alice$hair
#> [1] "black"
alice$greet()
#> こんにちは,Aliceです.
alice$set_hair("red")
# クラス定義にないフィールドは作れない
alice$age <- 40
#> Error in alice$age <- 40 : ...
# aliceとalice2は同一人物
alice2 <- alice
alice2$set_hair("blue")
alice$hair
#> [1] "blue"
name=“alice”
hair=“blue”
Person alice
Person alice2
- 15. 14
R6の機能
• public/private
– R6ではprivateフィールド,メソッドを作ることができる
• RCにはない機能
• オブジェクト自身のメソッドからしかアクセスできない
• (selfではなく)privateを通じてアクセスする
# privateを持つクラス
Person <- R6Class("Person",
private = list(age = NA, # 年齢はprivate
# 年齢のgetter
get_age = function() private$age),
public = list(name = NA, hair = "black",
initialize = function(name,hair,age) {
# 中略
# メソッドからはprivateフィールドが見える
private$age <- age
},
set_hair = function(hair) { #略 },
greet = function() { #略 },
# 外向け年齢はサバを読む
show_age = function() {
private$get_age() - 5
})
)
# オブジェクト作成
alice <- Person$new("Alice", "red", 40)
# age, get_age()は外からは見えない
alice$age
#> NULL
alice$get_age()
#> Error: attempt to apply non-function
alice$show_age()
#> [1] 35
- 16. 15
R6の機能
• 継承
– スーパークラスを inherit 引数で指定
– スーパークラスのメソッドはsuperを通じて呼べる
– パッケージをまたいだ継承が可能(portable class, RCではできない)
# inheritで継承
Employee <- R6Class("Employee",
inherit = Person,
private = list(tsurami = 0), # つらみ
public = list(
# メソッドオーバーライド
initialize = function(name, hair, age, tsurami) {
# スーパークラスはsuperで呼ぶ
super$initialize(name, hair, age)
private$tsurami <- tsurami
},
work = function() { private$tsurami <- private$tsurami + 1},
greet = function() {
if(private$tsurami > 5) paste0("社畜の", self$name, "です!")
else super$greet()
})
)
# Johnは社畜
john <- Employee$new("John", "black", 35, tsurami = 10)
john$greet()
#> 社畜のJohnです!
- 17. 16
R6の機能
• パフォーマンス
– R6はRCより速くて軽い
# RCとR6のパフォーマンス比較
library(microbenchmark)
# 適当なクラスを定義
R6 <- R6Class("R6", public = list(
x = NULL,
initialize = function(x = 1) self$x <- x,
inc = function(n = 1) self$x <- self$x + n)
)
RC <- setRefClass("RC",
fields = list(x = "numeric"),
methods = list(
initialize = function(x = 1) x <<- x,
inc = function(n = 1) x <<- x + n)
)
# オブジェクト生成
microbenchmark(
r6 <- R6$new(x = 100),
rc <- RC$new(x = 100))
#> Unit: microseconds
#> expr median neval
#> r6 <- R6$new(x = 100) 84.7825 100
#> rc <- RC$new(x = 100) 405.3950 100
# メソッド呼び出し
microbenchmark(r6$inc(), rc$inc())
#> Unit: microseconds
#> expr median neval
#> r6$inc() 16.065 100
#> rc$inc() 62.918 100
# オブジェクトサイズ
pryr::object_size(r6)
#> 3.94 kB
pryr::object_size(rc)
#> 465 kB
- 19. • R6パッケージとは
• R6を使う
– 既存のオブジェクト指向システム(S3, S4, RC)
– R6の機能
• R6の中身をのぞいてみる
– Rにおける環境
– R6Class()の実装
– $new()の実装
• まとめ
18
- 21. 20
Rにおける環境
• 環境(environment) = 関数を構成する要素のひとつ
– Rの関数の3つの構成要素
• formals: 引数
• body: 関数本体
• environment: 変数(名前)をオブジェクトに結びつけるデータ構造
g
x 1
g
x 2
environment(g) <- e
e
f <- function() {
x <- 1
function() {
x + 1
}
}
g <- f()
environment(g)
#> <environment: 0x0000000013ddc1b8>
# gは親環境にxを探しに行く
g()
#> [1] 2
# gの環境を変更する
e <- new.env()
e$x <- 2
environment(g) <- e
g()
#> [1] 3
<environment: 0x0000000013ddc1b8>
- 22. 21
Rにおける環境
• 環境は2つの要素からなる
– フレーム:名前をオブジェクトに結びつける
– 親環境(parent environment)
• 関数は親環境を辿っていって名前を探す
y <- 2
f <- function() {
x <- 1
function() {
x + y
}
}
h <- f()
h()
#> [1] 3
environment(h)
#> <environment: 0x000000001424c380>
parent.env(environment(h))
#> <environment: R_GlobalEnv>
h x 1
<environment: 0x000000001424c380>
y 2
globalenv()
parent.env()
- 23. 22
Rにおける環境
• 環境 ≃ reference semantics を持つリスト
– 関数とは無関係に,便利なデータ構造としても使える
– 環境がだんだん「オブジェクト」に見えてきたような…
# 環境e1を作成
e1 <- new.env()
e1$x <- 1
e1$x
#> [1] 1
# e2を変更するとe1も変わる
e2 <- e1
e2$x <- 2
e1$x
#> [1] 2
# 関数に渡した環境自体が変更される
g <- function(e) {
e$y <- 3
}
g(e1)
e1$y
#> [1] 3
# リストl1を作成
l1 <- list()
l1$x <- 1
l1$x
#> [1] 1
# l2を変更してもl1はそのまま
l2 <- l1
l2$x <- 2
l1$x
#> [1] 1
# 関数に渡したリスト自体は不変
f <- function(l) {
l$y <- 3
}
f(l1)
l1$y
#> NULL
- 24. • R6パッケージとは
• R6を使う
– 既存のオブジェクト指向システム(S3, S4, RC)
– R6の機能
• R6の中身をのぞいてみる
– Rにおける環境
– R6Class()の実装
– $new()の実装
• まとめ
23
- 25. 24
R6Class()の実装
• R6Class()の戻り値は?
Person <- R6Class("Person",
private = list(age = NA,
get_age = function() private$age),
public = list(name = NA, hair = "black",
initialize = function(name, hair, age) {
self$name <- name
self$hair <- hair
private$age <- age
},
set_hair = function(hair) {
self$hair <- hair
},
greet = function() {
paste0("こんにちは,", self$name, "です.")
},
show_age = function() {
private$get_age() - 5
})
)
Person
#> <Person> object generator
#> Public:
#> name: NA
#> hair: black
#> initialize: function
#> set_hair: function
#> greet: function
#> show_age: function
#> Private:
#> age: NA
#> get_age: function
#> Parent env: <environment: R_GlobalEnv>
#> Lock: TRUE
#> Portable: TRUE
- 26. 25
R6Class()の実装
• R6Class() の戻り値=環境
– R6Class()の引数やnew()を要素に持つgenerator環境がR6クラスの実体
# encapsulate()はR6Classの親環境をcapsuleにする
R6Class <- encapsulate(function(classname = NULL,
public = list(), private = NULL, inherit = NULL, ...) {
# ジェネレータオブジェクトの作成
generator <- new.env(parent = capsule)
generator$self <- generator # selfはgenerator自身
generator$classname <- classname
generator$public_fields <- get_nonfunctions(public)
generator$private_fields <- get_nonfunctions(private)
generator$public_methods <- get_functions(public)
generator$private_methods <- get_functions(private)
# generator_funsはnewを含む関数のリスト
# generator_funcs$newの環境をgeneratorにする
generator_funs <- assign_func_envs(generator_funs, generator)
# generator_funs$newをgenerator$new にコピー
list2env2(generator_funs, generator)
class(generator) <- "R6ClassGenerator"
generator
})
- 28. • R6パッケージとは
• R6を使う
– 既存のオブジェクト指向システム(S3, S4, RC)
– R6の機能
• R6の中身をのぞいてみる
– Rにおける環境
– R6Class()の実装
– $new()の実装
• まとめ
27
- 29. 28
$new()の実装
• $new()の戻り値=環境
– R6オブジェクトの実体はpublic_bind_env環境
– generatorからpublic_bind_envにフィールドとメソッドをコピーする
# publicフィールド・メソッドのみ,継承なしの場合
generator_funs$new <- function(...) {
# publicオブジェクトの束縛環境を作成
public_bind_env <- new.env(parent = emptyenv(), hash = FALSE)
# メソッドの環境を作成
enclos_env <- new.env(parent = parent_env, hash = FALSE)
# selfの実体はpublic_bind_env
enclos_env$self <- public_bind_env
# generator$public_methodsの環境をenclos_envにして
# public_bind_envにコピー
public_methods <- assign_func_envs(public_methods, enclos_env)
# フィールドとメソッドをpublic環境にコピー
list2env2(public_methods, envir = public_bind_env)
list2env2(public_fields, envir = public_bind_env)
# オブジェクト初期化
public_bind_env$initialize(...)
public_bind_env
}
- 33. • R6パッケージとは
• R6を使う
– 既存のオブジェクト指向システム(S3, S4, RC)
– R6の機能
• R6の中身をのぞいてみる
– Rにおける環境
– R6Class()の実装
– $new()の実装
• まとめ
32
- 34. まとめ
• R6は新しいオブジェクト指向システム
• R6の特徴
– 「普通の」オブジェクト指向っぽい
• reference semantics
• public / private
– RCより速くて軽い
• R6の実装
– 環境を活用したシンプルな実装
• 参考
– Introduction to R6 Classes
• http://cran.r-project.org/web/packages/R6/vignettes/Introduction.html
– Advanced R – Environments
• http://adv-r.had.co.nz/Environments.html
33