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の値が(内部でインクリメントされて)更新されているのがわかる。

要するに、

に近しいお話。