http://qiita.com/items/325bdf48f4f4885a86f1 と
http://qiita.com/items/35184390984975ec7c6d と
http://qiita.com/items/7fdb523a05a2e0b12f35 と
http://qiita.com/items/1cb4a7a05b026a0cf2bb と
http://qiita.com/items/a0a926c9e01aebb6261a の続き。
promiseは後回しにしておいて、名前空間と環境の話をします。
Rはパッケージシステムを用いていて、それぞれのパッケージはそれぞれの名前空間を必ず持っています(確かR 2.14.0 以降)。この話は、パッケージを作成する場合、または既存のパッケージの中の関数をハックしたい場合以外はあんまり役に立ちません。
パッケージ関連の環境のサーチパスと、関数呼び出しでの環境検索は、根っこは同じなんですが、分けて考えたほうがはるかに分かりやすいです。
関数にまつわる3つの環境
大事な話なのでもう一度書いておきます。
- 呼び出し時に作られる環境
- 呼び出し時に作られる環境の親となる環境
- 関数がある環境
関数呼び出しの時に何が起こるか。
例えば、コンソール上(環境は.GlobalEnv
)
> environment()
<environment: R_GlobalEnv>
> search()
[1] ".GlobalEnv" "tools:RGUI" "package:stats" "package:graphics" "package:grDevices" "package:utils" "package:datasets" "package:devtools" "MacJapanEnv"
[10] "package:methods" "Autoloads" "package:base"
で
> mean(1:3)
を実行したときの話を考えます。
-
.GlobalEnv
でmean
を探して、なければその親をたどっていく。 -
package:base
にmean
を見つける。 - 適当な環境Xを作って、その親として
mean
の環境をヒモづける。 - 環境X上で
mean
の中の式を実行する。
関数がある環境と関数の環境
サラッとmean
の環境と書きましたが、これは上の関数にまつわる3つの環境の中の3のことで、関数の環境です。
関数の環境はenvironment()
で、
> environment(mean)
<environment: namespace:base>
関数がある場所はfind()
で、
> find("mean")
[1] "package:base"
というように調べられます。はい、この二つ、関数がある環境(package:base
)と関数の環境(namespace:base
)は異なります。
普通の関数のある場所と関数の環境
ちょっとそれますが、.GlobalEnv
で関数を定義したときは、関数の環境も関数がある環境も両方共.GlobalEnv
です。
普通は、関数の環境は関数が作られた環境になるので、関数の環境と関数のある環境は一致しますが、これが常に成り立つわけではない、というのは前に説明したとおりです。
パッケージ内の関数を呼び出したときの変数検索
mean
の環境はnamespace:base
です。ですので、mean
を呼び出したときにどういう風に変数を探すかというと、
> debug(mean) # デバッグする
> mean(1:3) # 呼び出す
debugging in: mean(1:3) # デバッガに入る
debug: UseMethod("mean")
Browse[2]> environment() # 今の環境。これは関数呼び出しで適当に作った物
<environment: 0x11903b668>
Browse[2]> parent.env(environment()) # その親。namespace:baseですね。
<environment: namespace:base>
Browse[2]> parent.env(parent.env(environment())) # その親。
<environment: R_GlobalEnv>
Browse[2]> parent.env(parent.env(parent.env(environment()))) # その親。
<environment: 0x101e955c0>
attr(,"name")
[1] "tools:RGUI"
Browse[2]> parent.env(parent.env(parent.env(parent.env(environment())))) # その親
<environment: package:stats>
attr(,"name")
[1] "package:stats"
attr(,"path")
[1] "/Library/Frameworks/R.framework/Versions/2.15/Resources/library/stats"
という感じです。
正しく書くと、
- 関数呼び出しで作られた環境
- 関数の環境(パッケージの名前空間)
- .GlobalEnv
- 以降、
seaerch()
と同じ道をたどる。
となります。実際には、baseパッケージ以外の時は2のあとにbaseパッケージ名前空間が入ります。
面倒なので最後まで環境をたどる関数を定義します。
> f <- function(e = parent.frame()) while (!identical(e, emptyenv())) {print(environmentName(e)); e <- parent.env(e)}
> f()
[1] "R_GlobalEnv"
[1] "tools:RGUI"
[1] "package:stats"
[1] "package:graphics"
[1] "package:grDevices"
[1] "package:utils"
[1] "package:datasets"
[1] "package:devtools"
[1] "MacJapanEnv"
[1] "package:methods"
[1] "Autoloads"
[1] "base"
mean
の呼び出しでは
> debug(mean)
> mean(1)
debugging in: mean(1)
debug: UseMethod("mean")
Browse[2]> f()
[1] "" # これ適当に作られるやつ
[1] "base" # base名前空間
[1] "R_GlobalEnv"
[1] "tools:RGUI"
[1] "package:stats"
[1] "package:graphics"
[1] "package:grDevices"
[1] "package:utils"
[1] "package:datasets"
[1] "package:devtools"
[1] "MacJapanEnv"
[1] "package:methods"
[1] "Autoloads"
[1] "base" # package:base
stats
パッケージのrnorm
の場合はこうなります
rowse[2]> debug(rnorm)
Browse[2]> rnorm(1)
debugging in: rnorm(1)
debug: .Internal(rnorm(n, mean, sd))
Browse[4]> f()
[1] "" # 適当に作られたやつ
[1] "stats" # stats名前空間
[1] "imports:stats" # stats名前空間へのインポート
[1] "base" # base名前空間
[1] "R_GlobalEnv"
[1] "tools:RGUI"
[1] "package:stats"
[1] "package:graphics"
[1] "package:grDevices"
[1] "package:utils"
[1] "package:datasets"
[1] "package:devtools"
[1] "MacJapanEnv"
[1] "package:methods"
[1] "Autoloads"
[1] "base" # package:base
という感じです。
インポート???
それはまた別の機会に説明します。
つづく・・・