読者です 読者をやめる 読者になる 読者になる

Mukku John Blog

取り組んでいること を つらつら と

Rプログラミング入門 12回目

環境 前編

最初、環境とはなんぞや?と思っていましたが
なんてことないオブジェクトのスコープの話。

下準備

この2つの関数の改修を通じて、
環境への理解を深めていくため、事前に作成しておきます。
(過去記事でも、何度も登場している自作関数です。)

deal <- function(cards){
  cards[1, ]
}
shuffle <- function(cards){
  random <- sample(1:52, size = 52)
  cards[random, ]
}

環境

環境とは、オブジェクトを格納する場所のようなイメージです。
まずは今の環境がどうなっているのか確認してみます。

> library(pryr)
> parenvs(all = TRUE)
   label                                name                   
1  <environment: R_GlobalEnv>           ""                     
2  <environment: package:pryr>          "package:pryr"         
3  <environment: 0x000000000c9d86a8>    "tools:rstudio"        
4  <environment: package:RevoUtilsMath> "package:RevoUtilsMath"
5  <environment: package:RevoUtils>     "package:RevoUtils"    
6  <environment: package:RevoMods>      "package:RevoMods"     
7  <environment: package:RevoScaleR>    "package:RevoScaleR"   
8  <environment: package:lattice>       "package:lattice"      
9  <environment: package:rpart>         "package:rpart"        
10 <environment: package:stats>         "package:stats"        
11 <environment: package:graphics>      "package:graphics"     
12 <environment: package:grDevices>     "package:grDevices"    
13 <environment: package:utils>         "package:utils"        
14 <environment: package:datasets>      "package:datasets"     
15 <environment: package:methods>       "package:methods"      
16 <environment: 0x000000000a808d90>    "Autoloads"            
17 <environment: base>                  ""                     
18 <environment: R_EmptyEnv>            ""

この出力から分かる事は、このイメージになります。
最下位の環境=R_GlobalEnv、最上位の環境=R_EmptyEnvです。
f:id:MukkuJohn:20160713211520p:plain

Rの環境は、このイメージ通り、上側=親側への参照を持ちます。
下側=子側への参照はありません。

環境の操作

この環境の情報を確認するために、as.environment関数があります。
試しにこの環境の情報を確認してみます。

2  <environment: package:pryr>          "package:pryr" 

使い方は、nameを引数で渡します。

> as.environment("package:pryr")
<environment: package:pryr>
attr(,"name")
[1] "package:pryr"
attr(,"path")
[1] "C:/■■■/R/win-library/3.2/pryr"

・・・へぇ。。。で?です。

下の3つの環境は特別扱いらしく、環境を確認するために専用関数があります。
nameがないから、as.environment関数が使えないからでしょうね。

   label                                name                   
1  <environment: R_GlobalEnv>           ""                     
17 <environment: base>                  ""                     
18 <environment: R_EmptyEnv>            ""
> globalenv()
<environment: R_GlobalEnv>
> baseenv()
<environment: base>
> emptyenv()
<environment: R_EmptyEnv>

parent.env関数を使うと、親の環境が確認できます。
R_GlobalEnv環境の親環境を確認してみます。

> parenv(globalenv())
<environment: package:pryr>
attr(,"name")
[1] "package:pryr"
attr(,"path")
[1] "C:/■■■/R/win-library/3.2/pryr"

出力結果は、この結果と同じになります。

as.environment("package:pryr")

環境に格納されているオブジェクトは、ls関数またはls.str関数で確認できます。
ls関数から確認してみます。

> ls(emptyenv())
character(0)
> ls(globalenv())
 [1] "a"        "cards"    "deak"     "deal"     "DECK"     "foo"      "new"     
 [8] "roll"     "roll2"    "rolls"    "setup"    "shffle"   "show_env" "shuffle" 
[15] "x"        "y"      

格納されているオブジェクト名が返却されます。

ls.str関数です。

> ls.str(globalenv())
a : 'data.frame':	52 obs. of  3 variables:
 $ face : chr  "seven" "ace" "queen" "two" ...
 $ suit : chr  "hearts" "diamonds" "clubs" "spades" ...
 $ value: int  7 1 12 2 5 10 7 2 1 1 ...
cards : List of 2
 $ deal   :function ()  
 $ shuffle:function ()  
deak : function ()  
deal : function ()  
DECK : 'data.frame':	52 obs. of  3 variables:
 $ face : chr  "king" "queen" "jack" "ten" ...
 $ suit : chr  "spades" "spades" "spades" "spades" ...
 $ value: int  13 12 11 10 9 8 7 6 5 4 ...
foo :  chr "take me to your runtime"
new :  chr "Hello Active"
roll : function ()  
roll2 : function (bones = 1:6)  
rolls :  int [1:10000] 9 10 12 7 12 8 10 10 11 8 ...
setup : function (deck)  
shffle : function ()  
show_env : function (x = foo)  
shuffle : function ()  
x :  num [1:11] -1 -0.8 -0.6 -0.4 -0.2 0 0.2 0.4 0.6 0.8 ...
y :  num [1:11] -1 -0.512 -0.216 -0.064 -0.008 0 0.008 0.064 0.216 0.512 ...

ごった返しております。
単純に、今の環境になんのオブジェクトあるんだっけ?な時は
ls関数の方がよさげですね。

特定の環境のオブジェクトへのアクセスは$記法を利用します。
ls.str関数の実行結果の最初に出てきているaオブジェクトにアクセスします。

> head(globalenv()$a,3)
    face     suit value
46 seven   hearts     7
39   ace diamonds     1
15 queen    clubs    12

特定の環境のオブジェクトの更新はassign関数を利用します。

> assign("new","Hello Global",envir = globalenv())
> globalenv()$new
[1] "Hello Global"
アクティブ環境

Rは常に、1つの環境とやりとりをします。
そのやりとりしている環境をアクティブ環境と呼びます。

R_GlobalEnv環境(グローバル環境)は、
コマンドラインで作成したオブジェクト全てが格納されます。
なので、コマンドラインアクティブ環境R_GlobalEnv環境です。
f:id:MukkuJohn:20160713214559p:plain

スコープルール

オブジェクトを探索するとき、今までのイメージ図通りに探索を行います。
アクティブ環境 → 親の環境 → … → …
最上位の環境まで、オブジェクトが存在しない場合は、エラーになります。

つまり、親の環境に存在するオブジェクトは、子の環境から参照できます。

割り当て

同じ環境内に、同じ名前のオブジェクトが存在する場合は、さくっと更新されます。
こんな感じですね。

> new
[1] "Hello Global"
> new <- "Hello Active"
> new
[1] "Hello Active"

では、同じ環境で、関数の外と内に同じ名前のオブジェクトがある場合は
どうなのでしょう?

例えば、同じ環境でこのような状況を用意してみます。
まず、dieオブジェクトとdiceオブジェクトを作成します。

> die <- 11:16
> dice <- sample(die, size = 2, replace = TRUE)

次に、roll関数内に、
dieオブジェクトとdiceオブジェクトを作成します。

> roll <- function(){
+ die <- 1:6
+ dice <- sample(die, size = 2, replace = TRUE)
+ sum(dice)
+ }

roll関数を実行する前後で、オブジェクトの値を確認してみます。

> die
[1] 11 12 13 14 15 16
> dice
[1] 16 16
> roll()
[1] 8
> die
[1] 11 12 13 14 15 16
> dice
[1] 16 16

当然ですが、
roll関数のにあるdieオブジェクト、diceオブジェクトは
roll関数のにあるdieオブジェクト、diceオブジェクトとは
異なります。

関数内での一時オブジェクトによって、関数外のオブジェクトが
影響を受けるなんて、考えられません。


では、関数実行時の環境はどうなっているのでしょうか?

それは環境 後編に続きます。