なんじゃくにっき

プログラミングの話題中心。

R言語のクロージャー(Closure)と変数のスコープ

Wikipedia: クロージャ
http://ja.wikipedia.org/wiki/%E3%82%AF%E3%83%AD%E3%83%BC%E3%82%B8%E3%83%A3


【1】


x <- 10

func1 <- function () {
cat(x) # 10と出力される
}

関数内から外の変数にアクセスできます。


【2】


x <- 10

func1 <- function () {
x <- 20
cat(x) # 20と出力される
}
func1()

cat(x) # 10と出力される

関数内で定義された変数(ローカル変数)と同名のグローバル変数があった場合、
ローカル変数とグローバル変数は別々の値を保持します。
関数から抜けるとローカル変数は破棄されます。


【3】


x <- 10

func1 <- function () {
x <<- 20
}
func1()

cat(x) # 20と出力される

永続代入演算子<<-を用いれば、グローバル変数を書き換えることができます。
(正確には外のスコープの変数を書き換えます【5】参照。)

【4】


x <- 10

func1 <- function () {
x <- 20
func2 <- function() {
cat(x) # 20
}
func2()
}
func1()

関数は入れ子にすることができます。
入れ子にされた関数の場合、ローカル変数 → 1つ外の関数の変数 → ... → グローバル変数
という順番で変数を探しに行きます。
上の例では、xという名前の変数にアクセスしようとした場合、
最初にfunc1内でのxが見つかるので、その値を読み込みます。



【5】


x <- 10

func1 <- function () {
x <- 20
func2 <- function() {
x <<- 30
}
func2()
cat(x) # 30
}
func1()
cat(x) # 10

永続代入演算子<<-を用いれば、外の変数に代入することができます。
func2内で<<-を用いた場合、func1内の値に代入されています。
ではfunc2からグローバル変数にアクセスするにはどうすればいいのでしょうか?


【6】


x <- 10

func1 <- function () {
x <- 20
func2 <- function() {
assign('x', 30, env=.GlobalEnv)
cat(.GlobalEnv$x) # 30
}
func2()
cat(x) # 20
}
func1()
cat(x) # 30

assignを使って環境を指定すればグローバル変数を書き換えることができます。
また、グローバル変数から値を読み込むには、.GlobalEnv$xのようにします。

assignについては下記参照
http://stat.ethz.ch/R-manual/R-devel/library/base/html/assign.html


【7】


x <- 10

func1 <- function () {
x <- 20
func2 <- function() {
assign('x', 30, env=parent.frame())
}
func2()
cat(x) # 30
}
func1()

1つ外の環境にアクセスしたいときはparent.frame()を指定します。




以下、厳密な定義から外れるかもしれませんが適当な説明

環境 = 変数の集合
自身が定義された環境で変数を解決する構造を静的スコープと言います。
要は内側から変数を探しに行くのが静的スコープです。
関数と環境のセットの構造のことをクロージャ(Closure)と言います。