環境 (その2)

Rの環境について,2回に分けてまとめました.
第1回目は,環境全般とスコープ規則についてでした.
第2回目は,環境を操作する関数について.
なお,『Rプログラミングマニュアル』(間瀬茂, 2007年, 数理工学社) を参考にしました.
言語としてのRの基礎や,豊富な役立つtipsが載っており,とても参考になる本です.


環境へのアクセス

sys.関数群は,関数にともなう環境へのアクセス手段を,フレームスタックをたどって提供する.
.GlobalEnvはフレームリストのうちで番号0をもつ.関数が評価されると,フレームスタックが1ずつ増加していく.この評価環境にはsys.関数でアクセスできる.
一方,関数評価の親フレームは,かならずしも現在の評価フレームから1をひいたものではなく,関数が定義された環境でもなく,関数が呼び出された環境になる.

ff <- function(x) gg(x)
gg <- function(y) sys.status()
str(ff(1)) # objectの構造を表示する

> str(ff(1))
List of 3
 $ sys.calls  :Dotted pair list of 4
  ..$ : language str(ff(1))
  ..$ : language ff(1)
  ..$ : language gg(x)
  ..$ : language sys.status()
 $ sys.parents: int [1:4] 0 0 2 3     # gg()の評価環境の親フレーム番号
 $ sys.frames :Dotted pair list of 4
  ..$ :<environment: 0xda4bf0> 
  ..$ :<environment: 0xda4a4c> 
  ..$ :<environment: 0xda49c0> 
  ..$ :<environment: 0xda4988> 


以下のようなfoo1()を実行すると,その評価環境がスタック位置1にできる.
次に,foo1()内部で実行されるfoo2()はスタック位置2にその評価環境をつくる.
最後に,foo2()内部で実行されるfoo3()はスタック位置3にその評価環境をつくる.
foo3()の実行が終了するとその評価環境と内部変数x3は消滅する.
foo2()の実行が終了するとその評価環境と内部変数x2は消滅する.
foo1()の実行が終了するとその評価環境と内部変数x1は消滅し,大局的環境にもどる.大局的環境には変数x0がのこる.

foo1 <- function(){
  x1 <- foo2()
  cat(str(foo1))
  return(x1)
}

foo2 <- function(){
  x2 <- foo3()
  cat(str(foo3))
  return(x2)
}

foo3 <- function(){
  x3 <- pi
  cat(str(x3))
  return(x3)
}


ソースコードの読み込み

関数sys.source()はファイルからソースコードを読み込み,指定された環境に登録する.

foo <- function(x){
  # This is a function.
  return(x)
}
y <- 1:3
save("foo", "y", file = "foo.R", ascii = TRUE)
tempenv <- new.env()
sys.source("foo.R", envir = tempenv, keep.source = TRUE)
ls(env = tempenv)
tempenv$foo
tempenv$y

> ls(env = tempenv)
[1] "foo" "y"  
> tempenv$foo
function(x){
  # This is a function.
  return(x)
}
<environment: 0xd045ac>
> tempenv$y
[1] 1 2 3


永続的付値

永続的付値演算子 <<- は,対象の変数をスコープ規則にしたがって探索し,みつかった時点でそれに付値する.もしなければ,大局環境の.GlobalEnv中に変数を作成し,それに付値する.

> X
 エラー:  オブジェクト 'X' がありません 
> foo1 <- function(x){
+   bar <- function(y){
+     X <<- y
+     return(X)
+   }
+   X <- 0
+   bar(x)
+   cat(X)
+ }
> foo1(pi)
3.141593
> X
 エラー:  オブジェクト 'X' がありません 
# foo1()の評価環境中のXに値が付値されている.

> bar <- function(y){
+   X <<- y
+   return(X)
+ }
> 
> foo2 <- function(x){
+   X <- 0
+   bar(x)
+   cat(X)
+ }
> 
> foo2(1:3)
0
> X
[1] 1 2 3
# こちらは大局環境にXがつくられ,それに付値されている.

特定環境への値の付値

assign()関数はある環境中の与えられた名前のオブジェクトに値を付値する.
envir引数が与えられなかった場合,付値は現在アクティブな環境で行われる (よって,関数中でそのままassignしても,関数評価の終了とともにオブジェクトは消滅する).
もしinherit = TRUEが引数で与えられると,対象の変数がみつかるまで囲み環境が探索され,みつかった環境中で付値される.

> for(i in 1:6){
+ 	nam <- paste("n", i, sep = "")
+ 	assign(nam, 1:i)
+ }
> ls(pat = "n.$")
[1] "n1" "n2" "n3" "n4" "n5" "n6"
> myf <- function(x){
+   inner.f <- function(x){
+     assign("x.global", x^2, env = .GlobalEnv)
+   }
+   inner.f(x + 1)
+ }
> myf(3)
> x.global
[1] 16


特定環境からの値の取得

get()関数は与えられた名前のオブジェクトを指定された環境中で探し,値を返す.
pos引数には探索対象の環境を指定する.
mode引数には探したい型のオブジェクトが指定できる.

> get("get")
function (x, pos = -1, envir = as.environment(pos), mode = "any", 
    inherits = TRUE) 
.Internal(get(x, envir, mode, inherits))
<bytecode: 0xc784fc>
<environment: namespace:base>


特定環境へのアクセス

確認

exists()関数は,与えられた名前の変数が指定した環境に存在するかどうかを確認する.
where引数には探索対象の環境を指定する.
mode引数には探したい型のオブジェクトが指定できる.

> search()
 [1] ".GlobalEnv"        "tools:RGUI"        "package:stats"    
 [4] "package:graphics"  "package:grDevices" "package:utils"    
 [7] "package:datasets"  "MacJapanEnv"       "package:methods"  
[10] "Autoloads"         "package:base"     
> aa <- 1
> exists("aa", 1) # aaは位置1 (.GlobalEnv) にある
[1] TRUE
> exists("aa", 2) # aaは位置2 (tools:RGUI) にはない
[1] FALSE
> exists("lm", 3) # lmは位置3 (package:stats) にある
[1] TRUE


アクセス

特定環境中の変数には,2重コロン演算子でアクセスできる.

> pi <- 1:3
> pi
[1] 1 2 3
> base::pi # 本来の値が返される
[1] 3.141593

パッケージの場合は,読み込んだうえで値を返してくれる.

> head(SpaceShuttle)
 以下にエラー head(SpaceShuttle) :  オブジェクト 'SpaceShuttle' がありません 
> head(vcd::SpaceShuttle)
  FlightNumber Temperature Pressure Fail nFailures Damage
1            1          66       50   no         0      0
2            2          70       50  yes         1      4
3            3          69       50   no         0      0
4            4          80       50 <NA>        NA     NA
5            5          68       50   no         0      0
6            6          67       50   no         0      0


名前空間

attachNamespaces(),loadNamespaces(),loadedNamespaces()などの関数で名前空間を操作できる.これらはlibrary()関数の内部で使われている.

> loadedNamespaces()
[1] "base"      "graphics"  "grDevices" "MASS"      "methods"  
[6] "stats"     "utils"     "vcd"  


パッケージ中の変数にアクセス

pkg::name は,パッケージpkgのエクスポートされた変数nameの値を返す.
pkg:::name は,パッケージpkgの内部変数nameの値を返す.

> base::"+"
function (e1, e2)  .Primitive("+")


そのほか

topenv()関数でトップレベルの環境をみつける.


同じ環境を複数登録できる.しかし実行速度が遅くなるので避けるべき.

> attach(iris); attach(iris); attach(iris)
> search()
 [1] ".GlobalEnv"        "iris"              "iris"             
 [4] "iris"              "tools:RGUI"        "package:stats"    
 [7] "package:graphics"  "package:grDevices" "package:utils"    
[10] "package:datasets"  "MacJapanEnv"       "package:methods"  
[13] "Autoloads"         "package:base"     


多数の関数が共有するオブジェクトは,一次作業環境に置いておくと便利.
コピーをつくらずに直接参照でき,メモリの節約にもなる.

> tempenv <- new.env()
> assign("aa", 1, env = tempenv)
> foo <- function(x){x^2}
> assign("foo", foo, env = tempenv)
> rm(foo)
> aa
 エラー:  オブジェクト 'aa' がありません 
> get("aa", env = tempenv)
[1] 1
> get("foo", env = tempenv)
function(x){x^2}
> tempenv$foo(4) # リスト風にアクセスできる
[1] 16