Help us understand the problem. What is going on with this article?

(Rの)環境問題について その6。

More than 5 years have passed since last update.

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つの環境

大事な話なのでもう一度書いておきます。

  1. 呼び出し時に作られる環境
  2. 呼び出し時に作られる環境の親となる環境
  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)

を実行したときの話を考えます。

  1. .GlobalEnvでmeanを探して、なければその親をたどっていく。
  2. package:basemeanを見つける。
  3. 適当な環境Xを作って、その親としてmeanの環境をヒモづける。
  4. 環境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"

という感じです。

正しく書くと、
1. 関数呼び出しで作られた環境
2. 関数の環境(パッケージの名前空間)
3. .GlobalEnv
4. 以降、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

という感じです。

インポート???

それはまた別の機会に説明します。

つづく・・・

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away