Rはクラス(class)がイケてないので、毎度クロージャ(Closure, 閉包)でごまかす俺が酷い目にあった件
Rのクラスはあまりイケてないというか、
にも記載があるように、S3/S4/S5/S6という4つの書き方が乱立している状況で、どれを使ったらいいんじゃい状態なので、自分でコードを書かなきゃいけない時は俺はほとんど使っていない。なんで、クラスを使った際と似たような動作、クラス/メンバー変数を持たせた場合のような動作をさせるために、その代替としてクロージャーを使うわけです。例えばクラス変数Xとしてその値を3に固定して適当なシミュレーションを行いたいような状況の場合、
make_simulator <- function(x) { X <- x function(){ #一様乱数を一個もってきてそれをX倍して返却するシミュレーション X*runif(1) } }
というように一旦コンストラクタ的な"関数を生成する関数"を定義して、それを用いて、その中の変数Xを固定するようにするわけです。実際には以下のように使用する。
> simulator <- make_simulator(3) > simulator() [1] 1.932478 > simulator() [1] 0.1749089 > simulator() [1] 0.9162946
自作するコードにおいては、だいたい計算条件を"あらかじめ固定"してシミュレーションさせることが多いので、これで事足りていたんだが、クロージャの中で値(a)の更新する、つまりメンバ変数的な振る舞いをさせるために以下のようなコードを書いて実行させてみると・・・
make_f <- function() { #メンバー変数的にふるまって欲しいmake_fの環境に束縛されている変数a a <- 1 function() { print(a) a <- a + 1 print(a) } }
> f() [1] 1 [1] 2 > f() [1] 1 [1] 2 > f() [1] 1 [1] 2
うへぇ、aの値が更新されているようなコードを書いたつもりが、実は更新されていないという罠。こういうときはちゃんと”一個上の環境(ここではmake_f関数)のaに代入する"という意味で、永続代入演算子<<-を使わないとだめだということです。
make_f <- function() { a <- 1 function() { print(a) a <<- a + 1 print(a) } }
> f <- make_f() > f() [1] 1 [1] 2 > f() [1] 2 [1] 3 > f() [1] 3 [1] 4
ちゃんとfを実行するたびにaの値が(内部でインクリメントされて)更新されているのがわかる。
要するに、
に近しいお話。