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 と
http://qiita.com/items/6a2f6b7e21cd00f5098e の続き。
パッケージにまつわる環境
パッケージorzにまつわる環境は二つ有ります。
- namespace:orz
- package:orz
通常のサーチパスでアクセスするのは2のpackage:orz
です。
パッケージ内の関数を呼び出した時には、1のnamespace:orz
内が先にアクセスされます。
なんでこんなややこしい事になってるのか、っていう話になります。
パッケージは色々隠してる
Rには無数のパッケージが有ります。各パッケージはそれぞれ勝手に変数の名前をつけています。名前空間を導入する意義は、名前の衝突を防ぐことです。このためには、外からパッケージにアクセスできる変数を出来る限り必要最小限にするべきです、というのが一般論です。
パッケージを作る際には、パッケージ内部からしか使わない変数(主に補助的な関数)をたくさん作ります。これにパッケージ外からアクセスできると、名前衝突の可能性が超高まります。なので、パッケージ作成者は外からアクセスして欲しい変数を指定することで、それ以外の変数にはアクセスできないようにします。
大雑把に言うと、pakcage:orz
には、外からアクセスできる変数のみが、namespace:orz
には、そうではなくパッケージ内部で作成されたすべての変数がある、と考えてもらったらいいと思います。
これは実際に以下のように調べられます。
namespace:orz
の場合。
> e <- environment(rnorm)
> e # rnormの環境、つまり namespace:stats
<environment: namespace:stats>
> head(ls(e, all = TRUE)) # 環境内の変数を全部リストアップして(先頭のみ表示)
[1] ".__NAMESPACE__." ".__S3MethodsTable__." ".add.dendrInd" ".asSparse" ".cbind.ts" ".check_vars_numeric"
> length(ls(e, all = TRUE)) # 環境内の変数の数
[1] 1003
package:orz
の場合。
> g <- as.environment("package:stats")
> g # searchパス上のpackage:stats環境
<environment: package:stats>
attr(,"name")
[1] "package:stats"
attr(,"path")
[1] "/Library/Frameworks/R.framework/Versions/2.15/Resources/library/stats"
> head(ls(g, all = TRUE)) # 同じようにリストアップ。
[1] ".checkMFClasses" ".getXlevels" ".MFclass" "acf" "acf2AR" "add.scope"
> length(ls(g, all = TRUE)) # 環境内の変数の数
[1] 505
というわけで、statsパッケージのnamespace環境の中には1003個の変数があって、このうち外からアクセス出来るpackage環境の中には505個ある、というのがわかると思います。
ね。
環境の中に隠された変数にアクセスする
FAQですが、じゃあ私はnamespace
の中だけにある変数にアクセス出来ないんですかー、ってよく聞かれます。できます。get
か:::
(3コロン演算子を使えばいいです)。
get
も隠された変数にもアクセスできます。これはどうなんでしょう?
> .asSparse
エラー: オブジェクト '.asSparse' がありません # 普通は見つからない
> stats:::.asSparse # トリプルコロン
function (m)
{
if (is.null(tryCatch(loadNamespace("Matrix"), error = function(e) NULL)))
stop("contr*(.., sparse=TRUE) needs package \"Matrix\" correctly installed")
as(m, "sparseMatrix")
}
<bytecode: 0x11986eea8>
<environment: namespace:stats>
> get(".asSparse", environment(rnorm)) # get
function (m)
{
if (is.null(tryCatch(loadNamespace("Matrix"), error = function(e) NULL)))
stop("contr*(.., sparse=TRUE) needs package \"Matrix\" correctly installed")
as(m, "sparseMatrix")
}
<bytecode: 0x11986eea8>
<environment: namespace:stats>
Imports/depends
ここまでくるとパッケージ作成講座で環境講座ではなくなってくるんですが、一応簡単に書いておきます。
Importされた関数群は外からは使えない
多くのパッケージは他のパッケージの機能を使います。これはつまり、他のパッケージの環境内の変数(関数)にアクセスする、ということになります。パッケージが他のパッケージをインポートしている場合、
> # 環境をたどる関数。
> f <- function(e = parent.frame()) while (!identical(e, emptyenv())) {print(environmentName(e)); e <- parent.env(e)}
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" # ←これこれ。
[1] "base"
# … snip
[1] "Autoloads"
[1] "base" # package:base
このimports:stats
には何があるのかというと、
> getNamespaceInfo("stats", "imports")
$base
[1] TRUE
$graphics
.filled.contour abline arrows assocplot axis Axis axis.Date axis.POSIXct axTicks barplot
".filled.contour" "abline" "arrows" "assocplot" "axis"
# 以下略
です。
注意することなんですが、あるパッケージのimports
環境は、外からアクセスできません。
Hmisc
パッケージのimports環境には、
> lapply(getNamespaceInfo("Hmisc", "imports"), head)
$base
[1] TRUE
$methods
.__C__.environment .__C__.externalptr .__C__.name .__C__.NULL .__C__.Other .__C__(
".__C__.environment" ".__C__.externalptr" ".__C__.name" ".__C__.NULL" ".__C__.Other" ".__C__("
$lattice
llines lpoints lsegments
"llines" "lpoints" "lsegments"
$cluster
clara
"clara"
とありますが、例えばlattice
のllines
は、library(Hmisc)
したあとにHmiscの中の関数からは呼び出されますが、library(lattice)
とかして明示的に読み込まない限り、それ以外の環境からは使えません。
dependsは要するにlibrary(それ)
一方、depends
の場合は、dependされているパッケージが、サーチパスの中に追加されます。ですので、そのdependされているパッケージのpackage環境にアクセスできるようになります。
> tools::pkgDepends("Hmisc")
$Depends # Hmiscがdependsしているパッケージ
[1] "graphics" "methods" "splines" "stats" "survival" "utils"
# … snip
こんな感じです。
> search() # サーチパス
[1] ".GlobalEnv" "tools:RGUI" "package:stats" "package:graphics" "package:grDevices" "package:utils" "package:datasets" "package:devtools" "MacJapanEnv"
[10] "package:methods" "Autoloads" "package:base"
> library(Hmisc) # Hmiscを読み込み
> search() # 増えてる。
[1] ".GlobalEnv" "package:Hmisc" "package:survival" "package:splines" "tools:RGUI" "package:stats" "package:graphics" "package:grDevices" "package:utils"
[10] "package:datasets" "package:devtools" "MacJapanEnv" "package:methods" "Autoloads" "package:base"
というわけで、servivalとsplinesがHmiscのあとに追加されています。ですので、servivalとsplinesの関数は普通に使えます。
これでパッケージ・名前空間関連はいいのかな。
あとは、名前空間内の関数を書き換えたり、という方法ですが、これはバッドノウハウなので別の機会にします。
なにか間違ってるところとかわからない所があったら教えてください。
つづく。次はpromiseやろう。