COLOR(magenta){SIZE(18){R の S4 クラス、メソッド入門}} COLOR(blue){SIZE(16){R ユーザー会資料 (2005.12.10) 間瀬茂}}~

//COLOR(red){SIZE(18){只今作業中!}}~

以下では、R のクラスとメソッドについて簡単に説明する。R のクラスとメソッドの実装方式には COLOR(blue){S3 (S 言語第3版)} 方式と、COLOR(blue){S4 (S 言語第4版)} 方式が現在併用されているが、順次 S4 方式に統一されて行くと思われる。S4 クラス・メソッドは本格的なCOLOR(blue){オブジェクト指向}機構を実現している。R の基本パッケージ COLOR(blue){stats4} は S4 クラス・メソッドを操作する統計関数からなる。~

COLOR(blue){クラス(class)}とはある構造を持つデータの集まり、COLOR(blue){メソッド(method)}とはクラスに対してある処理を行う関数である。R の統計関数は多くCOLOR(blue){総称的(generic)}であり、個別のクラスに対して実際に適当な処理を行うCOLOR(blue){メソッド関数}グループが多数存在する。総称的関数は、引数のクラスの素性に応じて適当なメソッド関数をCOLOR(blue){選択適用(method dispatch)}する。これが、単一の関数(例えば COLOR(red){plot}, COLOR(red){summary})を使うだけで、多種多様な結果が得られる R の便利なメカニズムである。

S 言語第四版に関する原典は [[データによるプログラミング:http://www.amazon.co.jp/exec/obidos/ASIN/462784381X/qid=1134296720/sr=1-15/ref=sr_1_2_15/503-2197480-9371954]] (日本語訳あり)です。しかし、説明は S-plus によっており、R と微妙に異なり、R ユーザーには結構わかりにくいような気がします。私の経験では一番の近道は methods パッケージ中の関数のヘルプドキュメントとその参考実行例コードを研究することのように思われます。

//しばらくの間修正・追加等はコメント経由でお願いします。
-例えば、e1071パッケージにあるsvmのオンラインヘルプ中のS3 method for classってそういう意味だったんですね。積年の疑問が氷解しました。 -- [[樋口]] &new{2005-12-14 (水) 18:18:52};

#comment
~
~
COLOR(blue){SIZE(20){目次}}
#contents
~
~
*COLOR(magenta){SIZE(18){R の S3 クラスとメソッド}}
*COLOR(magenta){SIZE(18){R の S3 クラスとメソッド}} [#ife0cbfb]

**COLOR(magenta){SIZE(16){S3クラスは本質的にオブジェクトのクラス属性(class atribute)文字列}}
**COLOR(magenta){SIZE(16){S3クラスは本質的にオブジェクトのクラス属性(class atribute)文字列}} [#a5078315]

S3クラスは本質的にオブジェクトのCOLOR(blue){クラス属性(class atribute)}文字列に過ぎない。それが該当クラスに相応しい構造を持つかどうかは基本的に作成コード・操作の責任となる。

***COLOR(magenta){SIZE(16){例: S3 クラス、総称的関数、メソッド関数群}}
***COLOR(magenta){SIZE(16){例: S3 クラス、総称的関数、メソッド関数群}} [#j607b905]
 > x <- lm(1:10~rnorm(10))    # 簡単な単回帰。クラス属性 ``lm'' のオブジェクト
 > class(x)                   # クラス属性
 [1] "lm"
 > summary(x)   # 総称的関数 summary の適用。実際はメソッド関数 summary.lm() が適用される
 Call:
 lm(formula = 1:10 ~ rnorm(10))
 Residuals:
     Min      1Q  Median      3Q     Max
 -4.3527 -2.2764  0.1823  2.4843  4.5825
 (...以下略...)
 
**COLOR(magenta){SIZE(16){S3メソッドの仕組み}}
**COLOR(magenta){SIZE(16){S3メソッドの仕組み}} [#kc7dfc58]

総称的関数はクラス属性文字列(だけ?)を頼りに、適当なメソッド関数を割り当てる。例えば plot 関数は引数がクラス属性 "ts" を持つことを確認すると、それようのメソッド関数 plot.ts を実際には起動する(COLOR(blue){メソッドの選択適用(method dispatch)})。

***COLOR(magenta){SIZE(16){例:総称的関数 plot の(S3)メソッド関数一覧}} 
***COLOR(magenta){SIZE(16){例:総称的関数 plot の(S3)メソッド関数一覧}} [#k245b52e]
 > methods(plot)   
  [1] plot.Date*          plot.HoltWinters*   plot.POSIXct*
  [4] plot.POSIXlt*       plot.TukeyHSD       plot.acf*
 .........................................................
 [31] plot.ts             plot.tskernel*
    Non-visible functions are asterisked

**COLOR(magenta){SIZE(16){S3クラスの「いい加減さ」}}
**COLOR(magenta){SIZE(16){S3クラスの「いい加減さ」}} [#k37f2ee4]
S3 クラス属性は「勝手に」与えることができる。

***COLOR(magenta){SIZE(16){例: S3 クラス属性は「勝手に」与えることができる}}
***COLOR(magenta){SIZE(16){例: S3 クラス属性は「勝手に」与えることができる}} [#o759b6bc]
 > y <- runif(10)
 > class(y) <- "lm"      # 騙す(線形回帰オブジェクトにする)
 > class(y)              # 騙された 
 [1] "lm"
 > print(y)              # 無意味な出力(しかしエラーにならない)
 Call:
 NULL
 No coefficients

**COLOR(magenta){SIZE(16){S3クラスの整合性チェック機能}}
**COLOR(magenta){SIZE(16){S3クラスの整合性チェック機能}} [#p0493d5d]

S3クラスにも多少のCOLOR(blue){整合性チェック機能}がある。無理にクラスを変更しようとすると、つじつま合わせの強制変換(それ用の関数が存在すれば)を試みる。

***COLOR(magenta){SIZE(16){例:S3クラスの強制変換。行列クラスを無理に文字列クラスに変更する}}
***COLOR(magenta){SIZE(16){例:S3クラスの強制変換。行列クラスを無理に文字列クラスに変更する}} [#oce22af6]
 > x <- matrix(1:4, 2, 2)
 > class(x)
 [1] "matrix"                   # 行列クラス
 > class(x) <- "numeric"        # 騙す(困った挙げ句に白旗)
 警告メッセージ: 強制変換により NA が生成されました
 
 # 文字列クラスを行列クラスに変更
 > y <- "matrix"
 > class(x)
 [1] "character"                # 文字列クラス
 > class(y) <- "matrix"         # 騙す(さすがに苦情)
 エラー:次元属性が長さ 2 でないかぎり(0 でした)、
 行列にクラスを設定するのは不正です


*COLOR(magenta){SIZE(18){R の S4 クラス}}
*COLOR(magenta){SIZE(18){R の S4 クラス}} [#h39bd201]

S4 クラスの定義には、それが含むべきデータであるCOLOR(blue){スロット(slot)}とそのCOLOR(blue){型(type)}が明示的にCOLOR(blue){表現(representation)}され、異なった型のデータを含まないようにCOLOR(blue){チェック機能}が働く。また、他のクラスをデータとしてCOLOR(blue){継承(inheritance)}することができる。また、不用意に再定義されないようにクラス定義をCOLOR(blue){封印(seal)}することができる。クラスのオブジェクトにはCOLOR(blue){原型(prototype)}を与えておくことができる。S4 クラス定義は適当なCOLOR(blue){パッケージ(環境)}に記録される。

新しいクラスは COLOR(red){setClass} 関数で定義する。名前付きスロットは COLOR(red){representation} 欄に、名前無しスロットはCOLOR(red){contains} 欄に書く。クラスの新しいオブジェクトはスロットの値を使って COLOR(red){new} 関数で生成する。クラス定義は COLOR(red){getClass} 関数で参照できる。 

***COLOR(magenta){SIZE(16){例:名前付きスロット id (文字列) と名前無しスロット(数値)を持つクラス定義}}
***COLOR(magenta){SIZE(16){例:名前付きスロット id (文字列) と名前無しスロット(数値)を持つクラス定義}} [#s9d01f0e]
 > setClass("numWithId", representation(id = "character"),
             contains = "numeric")
 [1] "numWithId"
 > isClass("numWithId"  # クラスかどうか検査。isClass(numWithId) ではエラー
 [1] TRUE
 > getClass("numWithId") # クラス定義を見る
 Slots:
 Name:      .Data        id     # 名前無しスロットは .Data という名前
 Class:   numeric character
  
 Extends:
 Class "numeric", from data part
 Class "vector", by class "numeric"

***COLOR(magenta){SIZE(16){例:クラス "numWithId" のオブジェクト(インスタンス)を作る}}
***COLOR(magenta){SIZE(16){例:クラス "numWithId" のオブジェクト(インスタンス)を作る}} [#h8ecd10c]
 # e <- new("numWithId", id = "3 random numbers", runif(3)) でもOK
 > ( e <- new("numWithId", runif(3), id = "3 random numbers") )
 An object of class "numWithId"
 [1] 0.8540727 0.1615458 0.8514492   # 名前無し数値スロット
 Slot "id":                          # 名前付き文字列スロット
 [1] "3 random random numbers"
 
 # クラス定義は既定でパッケージ(環境) 「.GlobalEnv」 に登録 
 # 名前なしスロットは隠し名「.Data」を持つ
 > str(e)
 Formal class 'numWithId' [package ".GlobalEnv"] with 2 slots
   ..@ .Data: num [1:3] 0.854 0.162 0.851
   ..@ id   : chr "3 random numbers"

***COLOR(magenta){SIZE(16){例: 二つの名前付きスロットを持つクラス}}
***COLOR(magenta){SIZE(16){例: 二つの名前付きスロットを持つクラス}} [#de137a55]
 > setClass("numWithId2", representation(id="character", num="numeric"))
 [1] "numWithId2"
 > e2 <- new("numWithId2", id="3 sequence", num=1:3) 
 > e2
 An object of class "numWithId2"
 Slot "id":                        # 名前付き文字列スロット
 [1] "3 sequence"
 Slot "num":                       # 名前付き数値スロット 
 [1] 1 2 3
 
 > str(e2)
 Formal class 'numWithId2' [package ".GlobalEnv"] with 2 slots
   ..@ id : chr "3 sequence"
   ..@ num: int [1:3] 1 2 3

**COLOR(magenta){SIZE(16){スロットの中身を抜き出す}}
**COLOR(magenta){SIZE(16){スロットの中身を抜き出す}} [#qd3d7874]

名前付きスロットの中身を抜き出すには「COLOR(red){@ 演算子}」を使う。名前なしスロットには疑似名 COLOR(red){ .Data} を用いて

COLOR(red){e @ .Data}, COLOR(red){e @ ".Data"}, COLOR(red){slot(e,".Data")}

等とする。

***COLOR(magenta){SIZE(16){例: スロットの中身を取り出す}}
***COLOR(magenta){SIZE(16){例: スロットの中身を取り出す}} [#x735d014]
 > e2 @ id                     # id スロットの中身を取り出す
 [1] "3 sequence"
 > e2 @ num                    # num スロットの中身を取り出す
 [1] 1 2 3
 > slot(e2, "id")  # 専用 slot 関数もある (slot(e2, id) はエラー)
 [1] "3 sequence"

**COLOR(magenta){SIZE(16){スロットへの付値}}
**COLOR(magenta){SIZE(16){スロットへの付値}} [#tb45a3bd]
スロットには適正なオブジェクトを付値できる。不適正な値でオブジェクトを生成したり、付値するとエラーになる(COLOR(blue){クラスの整合性検査機構})。

***COLOR(magenta){SIZE(16){例: スロットへの代入}}
***COLOR(magenta){SIZE(16){例: スロットへの代入}} [#ua0c780b]
 > ( e2@num <- rnorm(4) )
 [1] -0.37293172  0.08139447  1.77335648 -0.54012836
 
 # 数値スロットに行列を代入しようとする
 > ( e2 <- new("numWithId", id="3 sequence", num=matrix(1:4,2,2)) )
 以下にエラー validObject(.Object) : invalid class "numWithId" object: 
 invalid object for slot "num" in class "numWithId": 
 got class "matrix", should be or extend class "numeric"

***COLOR(magenta){SIZE(16){例: 行列クラスをスロットに持つクラスの定義}}
***COLOR(magenta){SIZE(16){例: 行列クラスをスロットに持つクラスの定義}} [#afb63904]
 > setClass("matWithId3", representation(id="character", mat="matrix"))
 [1] "matWithId3"
 > ( e3 <- new("matWithId3", id="2x2 matrix", mat=matrix(1:4,2,2)) )
 An object of class "matWithId3"
 Slot "id":
 [1] "2x2 matrix"
 Slot "mat":
      [,1] [,2]
 [1,]    1    3
 [2,]    2    4
 > e3@"mat"'      # スロット mat の中身。slot(e3, "mat") でも可
      [,1] [,2]
 [1,]    1    3
 [2,]    2    4
 > e3@mat[1,2]    # 当然行列操作が可能 
 [1] 3

***COLOR(magenta){SIZE(16){例: 指定されたクラス以外のクラスをスロットに代入するとエラーになる}}
***COLOR(magenta){SIZE(16){例: 指定されたクラス以外のクラスをスロットに代入するとエラーになる}} [#g9efcd9d]
 > setClass("numWithId", representation(id = "character"), contains = "numeric")
 [1] "numWithId"  
 > setClass("bar", representation(id = "character"), contains = "character")
 [1] "bar"
 > ebar <- new("bar", id = "abc", "def") # クラス "bar" のオブジェクト
 > e <- new("numWithId", id = "abc", ebar) # 名前無しスロットに指定外のクラスオブジェクトを代入
 Warning message:
 強制変換により NA が生成されました # エラーにはならないことを注意

**COLOR(magenta){SIZE(16){クラスの拡張}} 
**COLOR(magenta){SIZE(16){クラスの拡張}} [#ueebc51b]
「COLOR(red){contains 欄}」を用いて既にあるクラスをCOLOR(blue){拡張(extend)}するクラスを定義できる。既存のクラスは「COLOR(blue){上位クラス(super-class)}」、
拡張されたクラスは「COLOR(blue){下位クラス(sub-class)}」と呼ばれる。

***COLOR(magenta){SIZE(16){例: 数値クラスを拡張するクラス定義}}
***COLOR(magenta){SIZE(16){例: 数値クラスを拡張するクラス定義}} [#cad82f3b]
 > setClass("numWithId4", representation(id = "character"), contains = "numeric")
 [1] "numWithId4"
 > e4 <- new("numWithId4", id="3 numbers", 1:3) 
 
 # 結果は単に擬似(名無し)スロットが加わるだけ
 > str(e4)
 Formal class 'numWithId4' [package ".GlobalEnv"] with 2 slots
   ..@ .Data: int [1:3] 1 2 3  
   ..@ id   : chr "3 numbers"

***COLOR(magenta){SIZE(16){例: もう少しややこしい例}}
***COLOR(magenta){SIZE(16){例: もう少しややこしい例}} [#r2f85ad3]
 > setClass("numWithId", representation(id = "character"), contains = "numeric")
 [1] "numWithId"
   
 ## クラス "numWithId" を名前つきスロットと名前無しスロットに含むクラスの定義
 ##  最初は失敗(スロット名 id が同じためらしい)
 > setClass("foo", representation(id = "numWithId"), contains = "numWithId")
 以下にエラー.validExtends(class1, class2, classDef, classDef2, obj@simple) :
        class "foo" cannot extend class "numWithId": slots in class "foo" must 
        extend corresponding slots in class "numWithId": fails for id
 以下にエラーsetClass("foo", representation(id = "numWithId"), contains =  "numWithId") :
        error in contained classes ("numWithId") for class "foo"; 
        class definition removed from '.GlobalEnv'
 ## スロット名を idd に変えると成功
 > setClass("foo", representation(idd = "numWithId"), contains = "numWithId")
 [1] "foo"
 > e1 <- new("numWithId", id = "3 random numbers", runif(3))
 > e2 <- new("numWithId", id = "4 random numbers", runif(4))
 > efoo <- new("foo", idd = e1, e2) # クラス "foo" のオブジェクト作成
 > str(efoo)   # オブジェクトの内容を見る(結構ややこしい (^^;))
   Formal class 'foo' [package ".GlobalEnv"] with 3 slots
   ..@ .Data: num [1:4] 0.5930 0.0884 0.2011 0.7343
       (# これは名前無しスロットの名前無しスロットの中身)
   ..@ idd  :Formal class 'numWithId' [package ".GlobalEnv"] with 2 slots
   .. .. ..@ .Data: num [1:3] 0.4524 0.0967 0.4248
       (# これは名前 idd 付きスロットの名前無しスロットの中身)
   .. .. ..@ id   : chr "3 random numbers"
       (# これは名前 idd 付きスロットの名前 id 付きスロットの中身)
   ..@ id   : chr "4 random numbers"
       (# これは名前無しスロットの名前 id 付きスロットの中身)

 ## 以下順に中身を取り出す
 > efoo @ idd  # 名前 idd 付きスロットの中身ークラス "numWithId" のオブジェクト
 An object of class "numWithId"
 [1] 0.5805303 0.8349548 0.9335655
 Slot "id":
 [1] "3 random numbers"
 > (efoo @ idd) @ id     # 名前 idd 付きスロットの中身の、名前 id 付きスロットの中身
 [1] "3 random numbers"
 > (efoo @ idd) @ .Data    # 名前 idd 付きスロットの中身の、名前無しスロットの中身
 [1] 0.5805303 0.8349548 0.9335655
 > efoo @ id         # 名前 id 付きスロットの中身
 [1] "4 random numbers"
 > efoo @ .Data        # 名前無しスロットの中身
 [1] 0.3309665 0.7076782 0.9590298 0.7051191

 以上の例からわかるように efoo の構造は一見
 efoo ---> 名前付きスロット idd ---> 名前付きスロット id
      |                         |
      |                         ---> 名前無しスロット .Data
      ---> 名前無しスロット .Data ---> 名前付きスロット id
                                  |
                                  ---> 名前無しスロット .Data
 となるように思われるが、実際は次のようになっている
 efoo ---> 名前付きスロット idd ---> 名前付きスロット id
      |                         |
      |                         ---> 名前無しスロット .Data
      ---> 名前無しスロット .Data 
      |
     ---> 名前付きスロット id
    
**COLOR(magenta){SIZE(16){プロトタイプ}}
**COLOR(magenta){SIZE(16){プロトタイプ}} [#k1d60876]
スロットにはCOLOR(blue){プロトタイプ(既定値)}を与えることができる。

***COLOR(magenta){SIZE(16){例: プロトタイプ付きクラス}}
***COLOR(magenta){SIZE(16){例: プロトタイプ付きクラス}} [#rd71f224]
 > setClass("coord", representation(x="numeric", y="numeric"),
             prototype = list(x=0, y=0) )
 [1] "coord"
 
 > new("coord") 
 An object of class "coord"
 Slot "x":                   # プロトタイプ値が入っている
 [1] 0
 Slot "y":
 [1] 0
 
 > new("coord", x=1, y=1)    # スロット変数に値を指定
 An object of class "coord"
 Slot "x":                   # 指定した値が入っている
 [1] 1
 Slot "y":
 [1] 1

**COLOR(magenta){SIZE(16){クラスの継承}} 
**COLOR(magenta){SIZE(16){クラスの継承}} [#l35188bb]
クラス定義は既存クラスをその一部として含むことができる(COLOR(blue){クラスの継承})。含まれたクラスはCOLOR(blue){上位クラス}、含むクラスはCOLOR(blue){下位(拡張)クラス}という。上位クラスとしては、COLOR(blue){基本データ型}でもよい。下位クラスは上位クラスのスロットをそのスロットの一部として持つ。同じ名前のスロットがあってもエラーにはならないが、同じものとされてしまう。

***COLOR(magenta){SIZE(16){例: クラスの継承、拡張}}
***COLOR(magenta){SIZE(16){例: クラスの継承、拡張}} [#y3a3f2e6]
 > setClass("coord", representation(x="numeric", y="numeric"), 
             prototype = list(x=0, y=0))
 [1] "coord"
 > ( c1 <- new("coord", x=1, y=1) )
 An object of class "coord"
 Slot "x":
 [1] 1
  Slot "y":
 [1] 1
 # "coord" を継承するクラス "coord2"
 > setClass("coord2", representation(z="numeric", y="numeric"), 
             prototype = list(z=0), COLOR(red){contains="coord"})
             prototype = list(z=0), contains="coord")
 [1] "coord2"
 > ( c2 <- new("coord2", z=1, c1) )
 An object of class "coord2"          # 三つのスロットを持つ
 Slot "z":
 [1] 1
 Slot "y":
 [1] 1
 Slot "x":
 [1] 1
 # 下位クラスに上位クラスとおなじ名前のスロットがあると、同じ物とされる
 > setClass(COLOR(red){"coord3"}, representation(x="numeric"), 
             prototype = list(COLOR(red){x=0}), contains="coord")
 > setClass("coord3", representation(x="numeric"), 
             prototype = list(x=0), contains="coord")
 [1] "coord3"
 > new("coord3", x=2, c1)
 An object of class "coord3"
 Slot "x":
 [1] 2
 Slot "y":
 [1] 1

***COLOR(magenta){SIZE(16){例: 基本データ型 matrix を拡張するクラス}}
***COLOR(magenta){SIZE(16){例: 基本データ型 matrix を拡張するクラス}} [#gbcaea88]
 > setClass("foo", representation(x="numeric"), contains="matrix")
 [1] "foo"
 > new("foo", x=1, matrix(1:4,2,2))
 An object of class "foo"
      [,1] [,2]
 [1,]    1    3
 [2,]    2    4
 Slot "x":
 [1] 1

**COLOR(magenta){SIZE(16){クラス定義の封印}}
**COLOR(magenta){SIZE(16){クラス定義の封印}} [#z8c82597]
クラス定義は改変できないようにCOLOR(blue){封印(seal)}できる。

***COLOR(magenta){SIZE(16){例: クラス定義の封印}}
***COLOR(magenta){SIZE(16){例: クラス定義の封印}} [#xcdc0f7e]
 > setClass("3coord", representation(x="numeric",y="numeric",z="numeric"))
 [1] "3coord"
 # 定義を書き換えるのは自由
 > setClass("3coord", representation(x="numeric",y="numeric",w="numeric"))
 [1] "3coord"
 > sealClass("3coord", where=.GlobalEnv) # 定義を封印
 ## 書き換えようとするとエラーになる(封印を解除するのは? クラスをいったん消すのかな?)
 > setClass("3coord", representation(x="numeric",y="numeric",z="numeric"))
 以下にエラー setClass("3coord", representation(x = "numeric", y = "numeric",  :
      "3coord" has a sealed class definition and cannot be redefined
 
 # 定義済みクラスの一覧
 > ls()                         # ls ではオブジェクトのみが表示される 
 [1] "e"            "e1"           "e3"           "e4"           "e5"
 [6] "e55"          "last.warning"
 > getClasses(.GlobalEnv)       # .GlobalEnv 中のクラス一覧
 [1] "3coord"    "coord"     "coord2"    "matWithId" "numWithId"

*COLOR(magenta){SIZE(18){R の S4 メソッド}}
*COLOR(magenta){SIZE(18){R の S4 メソッド}} [#h04e5680]

S4 クラスに対する操作関数は、COLOR(blue){総称的(generic)関数}の指定と、それに対するCOLOR(blue){メソッド(method)関数}の定義からなる。総称的関数は引数オブジェクトのクラスの組合せから、最適のメソッド関数をCOLOR(blue){選択割り当て(method dispatch)}る。COLOR(red){callGeneric}  関数の使用法に注意。

***COLOR(magenta){SIZE(16){例: クラスに対するメソッドの定義}}
***COLOR(magenta){SIZE(16){例: クラスに対するメソッドの定義}} [#vb574c65]
 > setClass("track", representation(x="numeric", y="numeric"))
 [1] "track"
 > isGeneric("Arith")  # (組み込みの)算術演算総称的関数(群) Arith
 [1] TRUE
 # クラスと数値の算術演算メソッドの定義
 > 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"
 
 # 算術演算全てにメソッドが同時に定義されている
 > t1 <- new("track", x=1:4, y=sort(rnorm(4)))
 > t1 - 100  # 第一のメソッド(引き算)適用
 An object of class "track"
 Slot "x":
  [1]  1  2  3  4  
 Slot "y":
  [1] -101.36265 -101.00339 -100.81776 -100.71723
 > 100 - t1  # 第二のメソッド(引き算)適用
 An object of class "track"
 Slot "x":
  [1]  1  2  3  4 
 Slot "y":
  [1] 101.36265 101.00339 100.81776 100.71723
 > 1/t1      # 第二のメソッド(割算)適用
 An object of class "track"
 Slot "x":
  [1]  1  2  3  4
 Slot "y":
  [1] -0.7338617 -0.9966217 -1.2228547 -1.3942441
 
 # クラス同士の算術演算のメソッドの定義
 setMethod("Arith", c("track", "track"),
             function(e1, e2) {
               e1@x <- callGeneric(e1@x , e2@x);
               e1@y <- callGeneric(e1@y , e2@y);
               e1                                
          })
 
 > t1 <- new("track", x=1:4, y=rnorm(4))
 > t2 <- new("track", x=rep(2,4), y=runif(4))
 > t1 + t2
 An object of class "track"
 Slot "x":
 [1] 3 4 5 6
 Slot "y":
 [1]  0.9274807  1.2821877 -0.3399180  1.7301085

***COLOR(magenta){SIZE(16){例: 総称的関数 plotData の定義}}
***COLOR(magenta){SIZE(16){例: 総称的関数 plotData の定義}} [#b1ab3729]
 > plotData <- function(x, y, ...) plot(x, y, ...)
 > setGeneric("plotData")  # "plotData" は総称的関数と宣言
 [1] "plotData"
 
 # そのメソッドの定義(第二引数は無いと宣言) 
 > setMethod("plotData", signature(x = "track", y = "missing"),
             function(x, y, ...) plot(slot(x, "x"), slot(x, "y"), ...))
 [1] "plotData"
 > plotData(t1)
 > removeGeneric("plotData") # 総称的関数で無くする
 [1] TRUE

***COLOR(magenta){SIZE(16){例: 総称的関数のメソッドの定義}}
***COLOR(magenta){SIZE(16){例: 総称的関数のメソッドの定義}} [#a1fa0a4d]
 # 行列一つをスロットに持つクラス "foo" の定義
 > setClass("foo", representation(m = "matrix"))
 [1] "foo"
 > m1 <- matrix(1:12, 3, 4)
 > f1 = new("foo", m = m1)
 > f2 = new("foo", m = t(m1))
  
 # 二つのクラス "foo" 引数を持つ総称的関数のメソッドを定義
 > setMethod("%*%", c("foo", "foo"), function(x, y) callGeneric(x@m, y@m))
 [1] "%*%"
 > stopifnot(identical(f1 %*% f2, m1 %*% t(m1))) # 検査
 > removeMethods("%*%")                          # メソッド定義を取り除く
 [1] TRUE

***COLOR(magenta){SIZE(16){例: 特殊関数 "[" のメソッド定義}}
***COLOR(magenta){SIZE(16){例: 特殊関数 "[" のメソッド定義}} [#gfab1533]
 > setMethod("[", "track", function(x, i, j, ..., drop) {
     x@x <- x@x[i]
     x@y <- x@y[i]
     x
   })
 [1] "["
 > plot(t1[1:15])

***COLOR(magenta){SIZE(16){例: S4 クラスを引数に取る普通(総称的でない)関数を定義することはもちろん可能 }}
***COLOR(magenta){SIZE(16){例: S4 クラスを引数に取る普通(総称的でない)関数を定義することはもちろん可能 }} [#t7bfa29a]
 > setClass("numWithId", representation(id = "character"), contains = "numeric")
 [1] "numWithId"
 > e <- new("numWithId", id = "abc", runif(3))
 > foo <- function (x) x@id  # "唯の" 関数
 > foo(e)
 [1] "abc"
 > foo2 <- function (x) x@.Data[3] # "唯の" 関数その2
 > foo2(e)
 [1] 0.2612681

*COLOR(magenta){SIZE(16){メソッドとクラスのドキュメント}}
*COLOR(magenta){SIZE(16){メソッドとクラスのドキュメント}} [#p32ed409]

S4 クラスとメソッドには該当するドキュメント(もし作者がそれを提供していれば)を表示する COLOR(red){? 演算子}を用いた組み込み機能がある。また、そうしたドキュメントを作成するための補助機能がある。

***COLOR(magenta){SIZE(16){例: クラス・メソッドのドキュメント}}
***COLOR(magenta){SIZE(16){例: クラス・メソッドのドキュメント}} [#va1bbf83]
 # クラス "genericFunction" に対するドキュメントを得る
 > class ? genericFunction
 
 # initialize 関数に対するメソッドのドキュメントを得る
 > methods ? initialize
 
 # 関数呼び出し myFun(x, sqrt(wt)) を評価する際、この呼び出しに対して使われるであろう
 # メソッドに関する何らかのドキュメントを得る。呼び出し自体と同様に、一つのメソッドが
 # 選択され、そのメソッドに対するドキュメントが得られる
 > ?myFun(x, sqrt(wt))
 
 # 第一引数がクラス maybeNumber、第二引数が logical であるメソッドに対するドキュメント
 # 指定されたクラスに対応する一つのメソッドが選択され、そのドキュメントが得られる
 > method ? myFun("maybeNumber", "logical")
 
 # 特定の総称関数に対して定義されたメソッド用のドキュメントの骨格をもつ初期的で一般的な
 # ファイル 'myFun-methods.Rd' を作り出す
 > promptMethods("myFun")
 
 # このファイルを編集した上、参照するには次のようにする
 > methods ? myFun


*COLOR(magenta){SIZE(16){パッケージ methods 内のオブジェクト一覧 }}
*COLOR(magenta){SIZE(16){パッケージ methods 内のオブジェクト一覧 }} [#e121849c]

以下は library(help=methods) で得られる R (2.1.1) の基本パッケージ methods 中のオブジェクトの
一覧である。

 .BasicFunsList           組み込み・特殊関数のリスト
 Classes                  クラス定義解説
 Documentation            クラスとメソッドのオンラインドキュメントの利用と作成
 GenericFunctions         総称的関数操作用ツール
 LinearMethodsList-class  クラス "LinearMethodsList"
 MethodDefinition-class   メソッド定義を表現するクラス
 MethodWithNext-class     クラス MethodWithNext
 Methods                  メソッドの一般情報
 MethodsList-class        クラス Class MethodsList、総称的関数用のメソッドの表現
 ObjectsWithPackage-class  関連パッケージ名を持つ、ブジェクト名ベクトル
 SClassExtension-class    継承(拡張)関係を表すクラス
 as                       オブジェクトがあるクラスに属するように強制
 callNextMethod           継承メソッドを呼び出す
 character-class          基本データ型に対応するクラス
 classRepresentation-class   クラスオブジェクト
 environment-class        クラス "environment"
 fixPre1.8                バージョン 1.8 以前の R からセーブされたオブジェクトをフィックス
 genericFunction-class    総称的関数オブジェクト
 getClass                 クラス定義を得る
 getMethod                メソッド定義を得る、検査する
 getPackageName           与えられたパッケージに伴う名前
 hasArg                   呼出し中の引数を見る
 initialize-methods       あるクラスからの新しいオブジェクトを初期化するメソッド
 is                       あるクラスからのオブジェクトか?
 isSealedMethod           封印されたメソッド・クラスに対する検査
 language-class           未評価の言語オブジェクトを表現するクラス
 makeClassRepresentation  クラス定義を生成する
 new                      あるクラスからオブジェクトを生成する
 promptClass              形式的クラスのドキュメントに対するシェルを生成
 promptMethods            形式的メソッドのドキュメントに対するシェルを生成
 representation           クラス定義に対するプロトタイプや表現を構築
 setClass                 クラス定義を作成
 setClassUnion            他のクラスの合併として定義されたクラス
 setGeneric               新しい総称的関数を定義
 setMethod                メソッドを作成、保管
 setOldClass              古いスタイルに対する名前を指定
 show                     オブジェクトを示す
 showMethods              指定された関数に対する全てのメソッドを示す
 signature-class          メソッド定義に対する "signature" クラス
 slot                     形式的クラスからのオブジェクト中のスロット
 structure-class          基本構造に対するクラス
 traceable-class          トレースを制御するために内部的に使われるクラス
 validObject              オブジェクトの正統性を検査


//The object paradigm in R is different; I like to think of methods as
//orthogonal to classes, instead of nested within them.
//
//Probably what you want to try is
//
//# a generic function, to operate on different classes
//setGeneric("test", function( obj ) standardGeneric( "test" ))
//
//# a class, containing data
//setClass("connect", representation( value = "numeric" ))
//
//# a specific method, operating on "connect" objects
//setMethod("test", signature=c("connect"),
// function( obj ) { obj@value / 2 })
//
//# a new object of class "connect" with value slot assigned 44
//testObj <- new("connect", value=44 )
//# application of the generic function to an object of class "connect",
//# dispatched to the method operating on objects of class "connect"
//test( testObj )
//
//This kind of structuure makes some types of operations particularly
//natural in R -- dispatch on multiple arguments, for instance.

トップ   編集 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 検索 最終更新   ヘルプ   最終更新のRSS