R6 OOPライブラリ
Rでオブジェクト指向をするのに、他のOOP言語から来た方が一番とっつきやすいのが最新のR6ライブラリだと思います。(R6も慣れが必要だが、それ以前はもっとすごい)
以下など解説があります。
R6パッケージの紹介―機能と実装
はまりポイント1 Error: attempt to apply non-function
いろいろ使ってリファクタリングをしているうちに、奇妙なエラーに出くわしました。忘れてしまいそうなので、備忘録的に記しておきます。
require(R6)
classA <- R6Class(
classname = "classA",
public = list(
a = 10,
f1 = function(x) { x + self$a },
f2 = function(x) { x + self$b },
f3 = function(x) { x + self$fp1(x) }
),
private = list(
b = 100,
fp1 = function(x) { x+1 }
)
)
このクラスでf3メソッドを呼ぶと、以下のように、関数でないものをコールしているぞと怒られます。
> A$f3(2)
Error in A$f3(2) : attempt to apply non-function
ちゃんとR6の仕様を読んでいればわかるのですが、問題はselfという自オブジェクトの識別子はプライベートメンバには使用できず、privateという識別子を使う必要があるのです。
そもそもプライベートメンバにアクセスした場合R6はどのような挙動をしめすのでしょうか?
> A$a
[1] 10
> A$f1(1)
[1] 11
> A$b
NULL
> A$fp1(1)
Error: attempt to apply non-function
オブジェクトを外部から呼び出した場合、プライベートプロパティはNULLを返し、メソッドは上記のエラーを吐くようです。ちなみにf2を呼ぶと 数値とNULLの足し算になるため以下のような結果になります。
> A$f2(2)
numeric(0)
正しくは、self を privateに直せばOKでした。
はまりポイント2 All elements of public, private, and active must be named.
こんなエラーにも遭遇しました。
慣れた人はこんなバカなことはしないと思いますが、以下のようなクラス定義で出ます。
require(R6)
classA <- R6Class(
classname = "classA",
public = list(
a <- 10,
f1 <- function(x) { x + self$a }
)
)
関数を f <- function()というのに慣れていると、ついついメソッドとしてそのままコピペしたりして、このようなエラーを招きます。
Error in R6Class(classname = "classA", public = list(a <- 10, f1 <- function(x) { :
All elements of public, private, and active must be named.
メソッド定義はlistのメンバとして定義するので、listの名前付き要素指定のために <- ではなく = が必要です。
はまりポイント3 cannot add bindings to a locked environment
もう一つ、もっと簡単なものですが、privateメンバ変数がらみで、このようなエラーが出ます。
require(R6)
classA <- R6Class(
classname = "classA",
public = list(
a = 10,
f1 = function(x) { self$b <- x }
),
private = list(
b = 1
)
)
> A$f1(1)
Error in self$b <- 2 : cannot add bindings to a locked environment
locked environmentというのがよくわかりませんでしたが、R6のクラスは基本的には定義の変更がロックされた状態がunlockすると動的にメンバーの追加、削除ができるようです。privateメンバへのアクセス制御もこの仕組みが用いられているのかわかりませんが、このようなエラーはきっちりアクセス制御が効いている証拠ですね。
でも、やっぱりわかりにくい(泣)
ちゃんと読んでいればわかるものを、ついついはまってしまいました。