niszetの日記

アナログCMOS系雑用エンジニアが頑張る備忘録系日記

(R) ggprotoをちょっとだけ使ってみる。

ggplot2パッケージ内で多用されているので、これの挙動をちょっと見ておかないといけないので…

と言っても、まずはサンプルを動かすところからです。

www.rdocumentation.org

途中まで。

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)すれば f:id:niszet:20180404170601p:plain

となっています。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()したものを表示。作り直したので値は変わっていますが…。

f:id:niszet:20180404173105p:plain

f:id:niszet:20180404173124p:plain

こうしてみると、関数にはggprotp_methodというclass属性があり、DoublerDoublerと継承した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")

となってエラーになります。Doubleraddメソッド中で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!!