R5 クラス

R5 クラスは R で定義できるクラスの一種で、R 2.12 以上のバージョンで利用できる。R5 はリファレンスクラスとも呼ばれ、オブジェクトは参照渡しにより受け渡しを行う。

R5 クラスの定義

R5 クラスの定義は setRefClass を利用する。クラス名、定義フィールド、クラスのメソッドの順に定義する。次は、Asagao クラスを R5 で定義する例である。

クラスの定義

Asagao <- setRefClass(
  # クラス名
  Class = "Asagao",
  
  # フィールド
  fields = list (
    color      = "character",  # 花の色
    leaves     = "numeric",    # 葉っぱの枚数
    conditions = "numeric",    # 生育状態
    seeds      = "numeric"     # 種の数
  ),
  
  # メソッド
  methods = list (
    # 葉っぱの枚数から生育状態を計算して、その状態を condionts 変数に保存
    CalcConditions = function() {
      x <- 1.12
      y <- 31.2
      conditions <<- leaves * x - y
    },

    # 現在の生育状態から種子数を予測し、予測した種指数を seeds に保存
    EstimateSeeds = function() {
      seeds <<- round(conditions / 10 + 50)
    }
  )
)

フィールド値の取得と更新

クラスからオブジェクトを生成するときに new 関数を利用する。初期値がある場合は new 関数に引数として与える。

# 青い朝顔を作成
blue.asagao <- Asagao$new(color="blue", leaves=50)   # この方法を推奨

# 青い朝顔の生育状態を計算する
blue.asagao$CalcConditions()
blue.asagao$conditions
## [1] 24.8



# 赤い朝顔を作成
red.asagao <- Asagao$new()
red.asagao$color <- "red"                            # この方法による初期化は非推奨
red.asagao$leaves <- 100
# 赤い朝顔の生育状態を計算し、種指数を予測する
red.asagao$CalcConditions()
red.asagao$EstimateSeeds()
red.asagao$seeds
## [1] 58

# field を利用してフィールドを取り出す方法
red.asagao$field("leaves")
## [1] 100

オブジェクトのコピー

R5 クラスのオブジェクトをコピーする場合は copy メソッドを利用する。R5 クラスのオブジェクトはリファレンス(メモリーのアドレス・宛先)であるから、new_obj <- old_obj と実行しても、new_objold_obj は同じ宛先を指すため、両者全く同じものである。そのため、R5 クラスのオブジェクトをコピーするときは copy を利用する必要がある。

# <- を利用した場合
asagao.1 <- Asagao$new(color="red", leaves=50)
asagao.2 <- asagao.1

# asagao.2 を変更
asagao.2$color <- "blue"

# asagao.1 も変換されてしまう(asagao.2 と asagao.1 が同じ宛先であるため)
asagao.1$color
## [1] "blue"


# copy を利用して R5 クラスのオブジェクトをコピー
asagao.3 <- asagao.1$copy()
asagao.3$color <- "pink"

# asagao.3 を変換しても asagao.1 は変換されない
asagao.1$color
## [1] "blue"

コンストラクタ

setRefClass で定義された R5 クラスのコンストラクタは new である。コンストラクタの動作は initialize で定義する。クラスからオブジェクトを生成するときに、初期化しておきたいフィールドがあれば、initialize 内で定義する。

Asagao <- setRefClass(
  Class = "Asagao",
  
  fields = list (
    color      = "character",  # 花の色
    leaves     = "numeric",    # 葉っぱの枚数
    conditions = "numeric"     # 生育状態
  ),
  
  methods = list (
    # コンストラクタ
    initialize = function(color=NULL, leaves=NULL) {
      if (is.null(color)) {
        color <<- "skyblue"
      } else {
        color <<- color
      }
      if (is.null(leaves)) {
        leaves <<- 50
      } else {
        leaves <<- leaves
      }
    },

    CalcConditions = function() {
      conditions <<- leaves * 10 - 5
    }
  )
)

以下のように引数なしで new を実行したとき、colorleaves フィールドはそれぞれ skyblue50 に初期化される。

skyblue.asagao <- Asagao$new()
skyblue.asagao$color
## [1] "skyblue"

skyblue.asagao$leaves
## [1] 50

継承

他のクラスを継承するには contains で定義する。

Flower <- setRefClass(
  Class = "Flower",
  
  fields = list (
    color      = "character",
    leaves     = "numeric",  
    conditions = "numeric"   
  )
)

# Flower クラスを継承する Asagao クラス
Asagao <- setRefClass(
  Class = "Asagao",
  # 継承
  contains = c("Flower"),

  fields = list(
    species = "character"
  ),
  methods = list(
    getConditions = function() {
      conditions <<- 2 * leaves
      return(conditions) 
    }
  )
)

asagao <- Asagao$new(leaves=20, species="momomurasaki")
asagao$getConditions()
## [1] 40