読者です 読者をやめる 読者になる 読者になる

Rにおけるmethod dispatch問題

R

S4とか総称的関数を使い出すとたぶんどっかでぶちあたるのがmethod dispatch問題です。僕は去年の夏くらいから「よく分からないなー」と思ってました。

method dispatchというのを間違ってるかもしれないけど、分かりやすめに説明すると、

  • 総称関数が使われた(例えばplot)!!
  • plot.lmとかplot.defaultとかplot.tsとかいっぱいある
  • 色々型とかに合わせて使いわけるようにしたいけど、どういう風の処理させよう?

みたいなものを解決する手段のようです。

gdgd言う前に例を考えてみます。hogeというクラスを作ってみます。

> setClass("hoge", representation(x="numeric"))
[1] "hoge"

このクラスにはまだplotというメソッドは用意されていないので、エラーが出ます(S4型に対するplotメソッドは未定義、というほうがいいかな)。

> plot(new("hoge",x=1:10))
 以下にエラー as.double(y) : 
  cannot coerce type 'S4' to vector of type 'double'

なので、このクラス用のmethodを定義してやります。するとこんな感じでうまくいきます。

> setMethod("plot",signature(x="hoge",y="missing"),function(x,y,...){
+   plot(slot(x,"x"))
+ })
 新しい総称関数 "plot"".GlobalEnv" 中に生成します 
[1] "plot"
> 
> plot(new("hoge",x=1:10))

このhogeクラス用の関数plotを定義した後のplotはこんな感じになっています。

> plot
standardGeneric for "plot" defined from package "graphics"

function (x, y, ...) 
standardGeneric("plot")
<environment: 0x199c72c>
Methods may be defined for arguments: x, y
Use  showMethods("plot")  for currently available ones.

一方、この関数を定義する前のふつーのplotはこんな感じになっています。さっきの関数を定義することで変化している、ということが分かります。

> plot
function (x, y, ...) 
{
    if (is.function(x) && is.null(attr(x, "class"))) {
        if (missing(y)) 
            y <- NULL
        hasylab <- function(...) !all(is.na(pmatch(names(list(...)), 
            "ylab")))
        if (hasylab(...)) 
            plot.function(x, y, ...)
        else plot.function(x, y, ylab = paste(deparse(substitute(x)), 
            "(x)"), ...)
    }
    else UseMethod("plot")
}
<environment: namespace:graphics>