ggplot2パッケージ内で多用されているので、これの挙動をちょっと見ておかないといけないので…
と言っても、まずはサンプルを動かすところからです。
途中まで。
Adder <- ggproto("Adder", x = 0, add = function(self, n) { self$x <- self$x + n self$x } )
これは"Adder"という名前を持ち、内部に初期値0の変数x
(プロパティといった方が良いのか)をもち、add
というメソッドで内部の変数x
の値を引数n
の分プラスする、Adder
という名前の変数を作っているところですね。x=0
の部分は継承するggpoto
のオブジェクトを入れる(次の例)なのですが、これは変数x
を取り込んでいる感じですね。
Adderを表示(ggplot2:::print.ggproto)すると、
Adder #> <ggproto object: Class Adder> #> add: function #> x: 0
となり、View(Adder)
すれば
となっています。View()
はfunction
の見づらさに定評があるんだよなぁ…。構造を素直に表示しているんだけど…。
ちょっとびっくりです(?)が、作成したAdder
オブジェクトはenvironment
です(表示されているように)
is.environment(Adder) #> [1] TRUE
もちろん、ggproto
でもある
is.ggproto(Adder) #> [1] TRUE
add
メソッドを呼ぶ。
Adder$add(10) #> [1] 10 Adder$add(10) #> [1] 20
add
メソッドの戻り値にself$x
とあるので、足せば(足した後の)値が返ってきますね。
直接代入することも出来る。
Adder$x <- 0 Adder #> <ggproto object: Class Adder> #> add: function #> x: 0
PythonをはじめとするOOな言語と同じように、メソッドの第一引数はseltになっていますが、これは意味がある名前のようで(予約語かなぁ)
Adder <- ggproto("Adder", x = 0, add = function(hoge, n) { hoge$x <- hoge$x + n hoge$x } ) Adder$add(1.3) #> Error in hoge$x : $ operator is invalid for atomic vectors
のように、selfをhogeに変えてあげると無事にErrorになります。Pythonは確かなんでも良い名前だったはずだけど(うろ覚え)
次、Adder
を継承して、与えられた引数の2倍の値を足す、Doubler
というオブジェクトを作成。の前に、一旦Adderオブジェクトの値を確定させておきますね。
Adder$add(39) #> [1] 39 Adder #> <ggproto object: Class Adder> #> add: function #> x: 39
で、
Doubler <- ggproto("Doubler", Adder, add = function(self, n) { ggproto_parent(Adder, self)$add(n * 2) } )
さて、このときxの値はというと、
Doubler #> <ggproto object: Class Doubler, Adder> #> add: function #> x: 39 #> super: <ggproto object: Class Adder>
で、当然
Doubler$x #> [1] 39
です。ここで、
Doubler$add(10) #> [1] 59
となりますが、
Adder #> <ggproto object: Class Adder> #> add: function #> x: 39
で、内部の値は別々に保持しているのでAdderには影響ありません。Adderに足した時も同じようにDoublerには影響がないです。
一応、それぞれをVIew()
したものを表示。作り直したので値は変わっていますが…。
こうしてみると、関数にはggprotp_method
というclass
属性があり、Doubler
はDoubler
と継承したAdder
のclass属性も持っているggproto
であることがわかります。先日のメソッドディスパッチのところに書いたように、この表示の一番左側の値が一番最初に評価されるので、継承元(親)は後ろにいることになるわけですね。
さて、いたるところにenvironment
がありますが、self
以外はタブってなさそうですね。
しかし、
rm(Adder)
でAdder
オブジェクトを削除してからDoubler$add()
を実行すると、
Doubler$add(10) #> Error in structure(list(parent = parent, self = self), class = "ggproto_parent") : #> object 'Adder' not found #> Called from: structure(list(parent = parent, self = self), class = "ggproto_parent")
となってエラーになります。Doubler
はadd
メソッド中でggproto_parent
関数で親のメソッドを呼んでいるので、Adder
を消すと呼べなくてエラーなわけです。
なわけですが、実体のオブジェクトで継承しているんだよなー、これ。いわゆるクラスではない。
ちなみに、こんな感じで再度Adder
オブジェクトを作って、
Adder <- ggproto("Adder", x = 0, add = function(self, n) { self$x <- self$x + n*100 self$x } )
Doubler$add()
を呼べば、
Doubler$add(10) #> [1] 2000
となります。
とりあえず、サンプルコード動かしてみたレベルの話は以上。とりあえずGeom
などのオブジェクトの挙動を考えるにはこれだけ把握できていれば十分かな~。
メソッド呼び出しも、実際は環境オブジェクト中の変数にアクセスしていて、それを関数呼び出しの形で使用しているだけだし、データの構造が見えればあまり怖くはなさそう。
問題は、ggproto_parent
の挙動とか、サーチパスどこをたどるのかとかですが、まぁそれは追々(まだスコープ周りについても整理が出来ていないから…)
そろそろ本腰入れてggplot2の内容把握していこう…。
Enjoy!!