Successfully reported this slideshow.
Your SlideShare is downloading. ×

R6パッケージの紹介―機能と実装

Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad

Check these out next

1 of 34 Ad

More Related Content

Slideshows for you (20)

Advertisement

Similar to R6パッケージの紹介―機能と実装 (20)

Recently uploaded (20)

Advertisement

R6パッケージの紹介―機能と実装

  1. 1. R6パッケージの紹介:機能と実装 Tokyo.R #46 2015-02-21 @__nakamichi__
  2. 2. • R6パッケージとは • R6を使う – 既存のオブジェクト指向システム(S3, S4, RC) – R6の機能 • R6の中身をのぞいてみる – Rにおける環境 – R6Class()の実装 – $new()の実装 • まとめ 1
  3. 3. • R6パッケージとは • R6を使う – 既存のオブジェクト指向システム(S3, S4, RC) – R6の機能 • R6の中身をのぞいてみる – Rにおける環境 – R6Class()の実装 – $new()の実装 • まとめ 2
  4. 4. R6パッケージとは • Rの新しいオブジェクト指向システム • 作者:Winston Changさん@RStudio • R言語のコアではなくパッケージとして実装 • 主にRStudio関係のパッケージで 利用されている – dplyr – httr – shiny 3
  5. 5. R6パッケージとは • 既に3つもOOシステムがあるのに,なぜまたもう1つ? – S3, S4, RC (Reference Class) • R6の特徴 – より「普通の」オブジェクト指向っぽい • 「普通の」= Java, C++, Python, etc. • RCと同様のreference semantics • フィールド,メソッドにpublic / private が指定可能 • パッケージをまたいだ継承が可能 – RCより速くて軽い – 設計・実装が単純 • Rの基本的な機能だけを使って実装されている 4
  6. 6. • R6パッケージとは • R6を使う – 既存のオブジェクト指向システム(S3, S4, RC) – R6の機能 • R6の中身をのぞいてみる – Rにおける環境 – R6Class()の実装 – $new()の実装 • まとめ 5
  7. 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. 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. 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. 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. 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. 12. • R6パッケージとは • R6を使う – 既存のオブジェクト指向システム(S3, S4, RC) – R6の機能 • R6の中身をのぞいてみる – Rにおける環境 – R6Class()の実装 – $new()の実装 • まとめ 11
  13. 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. 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. 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. 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. 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
  18. 18. 17 R6の機能 • ここまでのまとめ • R6はRCと同様, reference semantics のOOシステム • R6は「普通の」オブジェクト指向っぽい – 簡潔なsyntax – public/privateの区別 – パッケージをまたいだ継承 • R6はRCより速くて軽い
  19. 19. • R6パッケージとは • R6を使う – 既存のオブジェクト指向システム(S3, S4, RC) – R6の機能 • R6の中身をのぞいてみる – Rにおける環境 – R6Class()の実装 – $new()の実装 • まとめ 18
  20. 20. 19 R6の中身をのぞいてみる • R6の仕組みを知りたい – R6Class()や$new()って何? – public / private の仕組みは? • 実はR6の実装は意外と簡単 – Rの基本的な機能だけで実装できる • 要は,reference semantics を持つデータ構造を 「普通の」OOPのオブジェクトらしく見えるように 仕立てあげればよい • reference semanticsを持つデータ構造 =「環境」
  21. 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. 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. 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. 24. • R6パッケージとは • R6を使う – 既存のオブジェクト指向システム(S3, S4, RC) – R6の機能 • R6の中身をのぞいてみる – Rにおける環境 – R6Class()の実装 – $new()の実装 • まとめ 23
  25. 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. 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 })
  27. 27. 26 R6Class()の実装 generator [Person] R6Class self classname public_fields public_methods private_methods private_fields new function “Person” R6パッケージ内部の ユーティリティ関数群 capsule list(name=NA, ...) list(initialize = function(name, ...), ...) NULL list(age=NA, ...) Person <- R6Class() R6Class()の引数
  28. 28. • R6パッケージとは • R6を使う – 既存のオブジェクト指向システム(S3, S4, RC) – R6の機能 • R6の中身をのぞいてみる – Rにおける環境 – R6Class()の実装 – $new()の実装 • まとめ 27
  29. 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 }
  30. 30. 29 $new()の実装 public_fieldsと public_methodsのコピー generator [Person] self classname public_fields public_methods private_methods private_fields new function alice <- Person$new() initialize set_hair name greet hair function function function “black” “Alice” enclos_env public_bind_env [alice] self emptyenv()
  31. 31. 30 $new()の実装 • privateがある場合:privateをpublicと別の環境として作成 enclos_env public_fieldsと public_methodsのコピー initialize set_hair name greet hair function function function “black” “Alice” public_bind_env [alice] self emptyenv() private_fieldsと private_methodsのコピー get_age age function 40 private_bind_env private
  32. 32. 31 $new()の実装 • スーパークラスがある場合:superをpublicとは別の環境として作成 enclos_env public_fieldsと public_methodsのコピー initialize set_hair name greet hair function function function “black” “Alice” public_bind_env [john] self emptyenv() super_bind_env super super_enclos_env self super greetfunction スーパークラスの スーパークラスを 参照 スーパークラスの public_fieldsと public_methodsのコピー
  33. 33. • R6パッケージとは • R6を使う – 既存のオブジェクト指向システム(S3, S4, RC) – R6の機能 • R6の中身をのぞいてみる – Rにおける環境 – R6Class()の実装 – $new()の実装 • まとめ 32
  34. 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

×