13
17

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Posted at

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にまつわる環境は二つ有ります。

  1. namespace:orz
  2. 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" 

とありますが、例えばlatticellinesは、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やろう。

13
17
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
17

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?