クラス・メソッド・総称的関数

クラス (class) 構造をもつオブジェクトで,クラス属性 (class attribute) という文字列がひとつまたは複数登録されている.Rの関数が返すオブジェクトはたいてい何らかのクラスに属し,特定の構造をもっている.また,特定のクラスオブジェクトを引数にとれる関数も数多くある.
異なるクラスオブジェクトに対する類似の処理を,ひとつの関数で代表させる機構が,総称的関数 (generic function) とメソッド選択適用 (method dispatch).総称的関数"function()"に,引数としてクラス"class"が与えられると,"function.class()"という名前の関数が探され適用される.

> # plot関数のメソッド関数一覧
> methods(plot)
 [1] plot.acf*           plot.data.frame*    plot.decomposed.ts*
 [4] plot.default        plot.dendrogram*    plot.density       
 [7] plot.ecdf           plot.factor*        plot.formula*      
[10] plot.function       plot.hclust*        plot.histogram*    
[13] plot.HoltWinters*   plot.isoreg*        plot.lm            
[16] plot.medpolish*     plot.mlm            plot.ppr*          
[19] plot.prcomp*        plot.princomp*      plot.profile.nls*  
[22] plot.spec           plot.stepfun        plot.stl*          
[25] plot.table*         plot.ts             plot.tskernel*     
[28] plot.TukeyHSD      

   Non-visible functions are asterisked
> 


S3クラス・メソッド

本質的には,クラス属性文字列の与えられたオブジェクトにすぎない.該当クラスにふさわしい構造をもつかどうかに関係なく与えることができてしまう.

> x <- runif(10)
> class(x)
[1] "numeric"
> class(x) <- "lm"
> class(x) # だませてしまう
[1] "lm"
> summary(x) # しかし無意味
 エラー: $ operator is invalid for atomic vectors
> 
y <- c("R", "language")
class(y)
class(y) <- "matrix" # 多少の整合性チェックはする

Rシステムに組み込みのS3総称的関数は以下で確認できる.

> names(.knownS3Generics)
 [1] "Math"           "Ops"            "Summary"        "Complex"       
 [5] "as.character"   "as.data.frame"  "as.environment" "as.matrix"     
 [9] "as.vector"      "cbind"          "labels"         "print"         
[13] "rbind"          "rep"            "seq"            "seq.int"       
[17] "solve"          "summary"        "t"              "edit"          
[21] "str"            "contour"        "hist"           "identify"      
[25] "image"          "lines"          "pairs"          "plot"          
[29] "points"         "text"           "add1"           "AIC"           
[33] "anova"          "biplot"         "coef"           "confint"       
[37] "deviance"       "df.residual"    "drop1"          "extractAIC"    
[41] "fitted"         "formula"        "logLik"         "model.frame"   
[45] "model.matrix"   "predict"        "profile"        "qqnorm"        
[49] "residuals"      "se.contrast"    "terms"          "update"        
[53] "vcov"          
> 


S4クラス・メソッド

含むべきデータであるスロット (slot) とその型 (type) が明示的に表現され,チェックがはたらくようになっている.また,他のクラスを継承 (inheritance) したり,クラス定義を変更されないように封印 (seal) することができる.クラスオブジェクトには原型 (prototype) を与えておくことができる.クラス定義はパッケージ (環境) に記録される.

S4クラスの定義

setClass()関数で定義,名前つきスロットはrepresentationに,名前なしスロットはcontainsに書く.そのクラス属性を持つ新しいオブジェクト (インスタンス) はnew()関数で生成する.

> setClass("ex", representation(id = "character"), contains = "numeric")
> getClass("ex")
Class "ex" [in ".GlobalEnv"]

Slots:
                          
Name:      .Data        id
Class:   numeric character

Extends: 
Class "numeric", from data part
Class "vector", by class "numeric", distance 2
> # 名前なしスロットは .Data として与えられる
> setClass("mat", representation(id = "character", num = "matrix"))
> getClass("mat")
Class "mat" [in ".GlobalEnv"]

Slots:
                          
Name:         id       num
Class: character    matrix
> 

クラス"ex"のインスタンスを作成

> (ex.1 <- new("ex", runif(3), id = "first 3"))
An object of class "ex"
[1] 0.3433830 0.8273612 0.7917083
Slot "id":
[1] "first 3"

> str(ex.1)
Formal class 'ex' [package ".GlobalEnv"] with 2 slots
  ..@ .Data: num [1:3] 0.343 0.827 0.792
  ..@ id   : chr "first 3"
> 

不適切なオブジェクトはスロットに代入できない.

> ex.2 <- new("ex", runif(3), id = numeric(2))
 以下にエラー validObject(.Object) : 
   不正なクラス “ex” オブジェクト:  invalid object for slot "id" in class "ex": got class "numeric", should be or extend class "character"
> 

クラススロットの操作

スロットの中身を抜き出すには @ 演算子を使う.または,slot(instance, ".Data") などのようにする.

> ex.1@id
[1] "first 3"
> ex.1@.Data
[1] 0.3433830 0.8273612 0.7917083
> slot(ex.1, "id")
[1] "first 3"
> 

スロットにオブジェクトを付値.整合性の検査機構がはたらく.

(> (ex.1@id <- "First 3")
[1] "First 3"
> ex.2 <- new("ex", representation(id = numeric(2), contains = 1:3))
 以下にエラー representation(id = numeric(2), contains = 1:3) : 
   representation の要素 1 は単一の文字列ではありません 
> 

クラスの拡張

containsは,すでにあるクラスから拡張 (etend) されたクラスと定義される.
既存のクラスは上位クラス (super-class),拡張されたクラスは下位クラス (sub-class) となる.

スロットのプロトタイプ

スロットにはプロトタイプ (既定値) を与えられる.

> setClass("prot", representation(id = "character", num = "numeric"),
+   prototype = list(id = "ND", num = 0))
> (prot.1 <- new("prot", id = "Carbon", num = 12))
An object of class "prot"
Slot "id":
[1] "Carbon"

Slot "num":
[1] 12

> (prot.2 <- new("prot"))
An object of class "prot"
Slot "id":
[1] "ND"

Slot "num":
[1] 0

> 

クラスの継承

既存のクラスをクラス定義に含めることができる.含まれたクラスは上位クラス,含むクラスは下位 (拡張) クラスとなる.

> setClass("prot2", representation(id2 = "character", num2 = "numeric"),
+   prototype = list(id2 = "YES", num2 = 1), contains = "prot")
> (prot2.1 <- new("prot2", id2 = "Nitrogen", num2 = 14, prot.1))
An object of class "prot2"
Slot "id2":
[1] "Nitrogen"

Slot "num2":
[1] 14

Slot "id":
[1] "Carbon"

Slot "num":
[1] 12

>

同じ名前のスロットがあると,同じものとされてしまう.

> setClass("prot3", representation(id = "character", num3 = "numeric"),
+   prototype = list(id = "YES", num3 = 1), contains = "prot")
> (prot3.1 <- new("prot3", id = "Oxigen", num3 = 16, prot.1))
An object of class "prot3"
Slot "id":
[1] "Oxigen"

Slot "num3":
[1] 16

Slot "num":
[1] 12

> 

クラス定義の封印

クラス定義を変更できないようにする.

> setClass("ex3", representation(x = "numeric", y = "numeric"))
> setClass("ex3", representation(x = "logical", z = "numeric"))
> sealClass("ex3", where = .GlobalEnv)
> setClass("ex3", representation(x = "numeric", y = "numeric"))
 以下にエラー setClass("ex3", representation(x = "numeric", y = "numeric")) : 
   "ex3" は封印されたクラス定義を持ち、再定義は出来ません 
> 

S4メソッド

総称的関数を指定して,それに対するメソッド関数を定義する.総称的関数は,引数オブジェクトのクラスの組み合わせに対応する最適なメソッド関数を探して,割り当てる.

> # クラス"track"を定義
> setClass("track", representation(x = "numeric", y = "numeric"))
> 
> # 組み込みの算術演算総称的関数Arithに対し,"track"クラスに対応するメソッドを定義
> setMethod("Arith", c("track", "numeric"),
+   function(e1, e2){e1@y <- callGeneric(e1@y, e2); e1})
[1] "Arith"
> # 逆も
> setMethod("Arith", c("numeric", "track"),
+   function(e1, e2){e2@y <- callGeneric(e1, e2@y); e2})
[1] "Arith"
> 
> # "track"クラスをもつインスタンス生成
> t1 <- new("track", x = 1:3, y = rnorm(1))
> t1 - 1 # 引き算メソッドが適用される
An object of class "track"
Slot "x":
[1] 1 2 3

Slot "y":
[1] -3.623696

> 1 / t1 # 割り算メソッドが適用される
An object of class "track"
Slot "x":
[1] 1 2 3

Slot "y":
[1] -0.3811417

>