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です。
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"
スコープルール
オブジェクトを探索するとき、今までのイメージ図通りに探索を行います。
アクティブ環境 → 親の環境 → … → …
最上位の環境まで、オブジェクトが存在しない場合は、エラーになります。
つまり、親の環境に存在するオブジェクトは、子の環境から参照できます。
割り当て
同じ環境内に、同じ名前のオブジェクトが存在する場合は、さくっと更新されます。
こんな感じですね。
> 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オブジェクトとは
異なります。
関数内での一時オブジェクトによって、関数外のオブジェクトが
影響を受けるなんて、考えられません。
では、関数実行時の環境はどうなっているのでしょうか?
それは環境 後編に続きます。