2011-09-24 14 views
8

私はJavaとPythonの背景を持っており、私は最近Rを学んでいます。Rは関数呼び出しでオブジェクトをどのように扱うのですか?

今日、私はRがJavaやPythonとはかなり異なったオブジェクトを扱っているようです。例えば

、以下のコード:

x <- c(1:10) 
print(x) 
sapply(1:10,function(i){ 
      x[i] = 4 
     }) 
print(x) 

コードは、以下の結果が得られる:

[1] 1 2 3 4 5 6 7 8 9 10 
[1] 1 2 3 4 5 6 7 8 9 10 

をしかし、私は変性するので、出力の2行目は、すべての「4」であることを期待しますサプリー関数のベクトル。

これは、Rはオブジェクトの参照ではなく関数呼び出しでオブジェクトのコピーを作成するということですか?

答えて

18

xは、グローバル環境では、ありませんあなたの関数で定義されています。

xなどの非ローカルオブジェクトを関数内で修正しようとすると、Rはオブジェクトのコピーを作成してコピーを変更するので、匿名関数を実行するたびにxのコピーが作成され、そのi番目のコンポーネント関数が終了すると、作成されたコピーは永遠に消えます。元のxは変更されていません。

x[i] <<- iと書く場合、またはx[i] <- 4; assign("x", x, .GlobalEnv)と書くと、Rはそれを書き戻します。

e <- environment() 
sapply(1:10, function(i) e$x[i] <- 4) 

または多分この:

sapply(1:10, function(i, e) e$x[i] <- 4, e = environment()) 

通常1は、このような書き込みません戻ってそれを書くための別の方法はxが格納された環境に、たとえば、eを設定し、これを実行することですR.むしろのコード1は、このような関数の出力として結果を生成する:

x <- sapply(1:10, function(i) 4) 

(実際にはこの場合一方はを書くことができます10)

加えた:。proto package一つは、これを行うことができ用い

場所4からxプロパティのi番目の成分をセットf方法。

library(proto) 

p <- proto(x = 1:10, f = function(., i) .$x[i] <- 4) 

for(i in seq_along(p$x)) p$f(i) 
p$x 

を追加しました:。我々は明示的xが格納された環境に合格した他のオプション

+0

ありがとう!しかし、なぜそのようなコードをRに書くのではないでしょうか?潜在的なリスクはあるのですか?私は他の言語の関数でグローバルオブジェクトを変更するのはかなり普通だと思います。 –

+3

関数型言語では、関数は副作用を持つことができません。 Rはそれほど厳密ではありませんが、R関数が副作用を制限するということはまだ真実です。あたかも別の言語で書いているかのように書くのではなく、むしろ使いたいと思った方法で作業する方が良いです。オブジェクトシステムはいくつかあります(S3、S4、参照クラス)。 S3が最も一般的に使用されています。 S4ははるかに複雑です。参照クラスは最近追加されたクラスです。特に、参照クラスを探索することができます。また、protoとR.oo(そしておそらく他のもの)という異なるパラダイムを提供するユーザー貢献パッケージもあります。 –

+0

@Spirit Plus '.GlobalEnv'の代わりに' parent.frame(3) 'を使って、サプリーが実行されたクロージャーにxを格納することができます。何がもっと安全でしょうか。 (なぜ3〜1匿名機能フレーム、2サプリーフレーム、3サプリーエンクロージャ) – mbq

7

はい、そうです。 R言語の定義を確認してください:4.3.3 Argument Evaluation

AFAIK、Rは実際にはデータを変更しようとするまで、つまりCopy-on-writeセマンティクスに従ったデータをコピーしません。

+0

Thanks Anatoliy!しかし、コピーされたデータが本当に大きい場合、コピー処理に時間とメモリが掛かりすぎるでしょうか?または、実際にデータをコピーするのではなく、関数呼び出しの最後に変更を中和するだけですか? –

+0

コピーがありますが、関数内の "x"は関数外のものと同じオブジェクトではありません。環境があり、呼び出し環境にはxが1つあり、関数環境にはxがあります。結果を代入するだけで、呼び出し環境で変更が表示されます。 –

0

sapplyの出力をオブジェクトに割り当てる必要があります。そうでない場合は、オブジェクトは消えます。 (それはまた.Last.valueに割り当てられますので、実際にあなたがそれを回復することができます)

x <- c(1:10) 
print(x) 
[1] 1 2 3 4 5 6 7 8 9 10 
x <- sapply(1:10,function(i){ 
      x[i] = 4 
     }) 
print(x) 
[1] 4 4 4 4 4 4 4 4 4 4 
+0

申し訳ありませんが、これは質問に答えず、ちょうど混乱しています。 – mbq

+0

今私は混乱している人です。 OPは4のベクトルを作成し、それから何もしなかった。彼が "x"を変更したければ、彼は割り当て操作を使う必要がありました。私はその質問に正確に答えたと思った。 –

+0

あなたは 'v <-applly(...、function(...){... v ...})'という構造体が、関数環境から親環境へ 'v'を何とかエクスポートすることを示唆しているようです。 Rがコール・バイ・コピーを行うのか、コール・バイ・リファレンスを行うのかについては、直接的に問題は言及されていない。 – mbq

3

x匿名関数の内部にある上に追加

ではないxですグローバル環境(あなたのワークスペース)。これは、匿名機能のローカルなxのコピーです。 Rが関数呼び出しでオブジェクトをコピーするというだけでは簡単ではありません。 Rは可能な限りコピーしないように努力しますが、いったんRがオブジェクトをコピーしなければならないものを変更してしまいます。

@DWinが指摘するように、を変更されているxのこのコピーされたバージョンはsapply()呼び出しによって返され、あなたの主張の出力は私が得るものではありません。

> x <- c(1:10) 
> print(x) 
[1] 1 2 3 4 5 6 7 8 9 10 
> sapply(1:10,function(i){ 
+    x[i] = 4 
+   }) 
[1] 4 4 4 4 4 4 4 4 4 4 
> print(x) 
[1] 1 2 3 4 5 6 7 8 9 10 

明らかに、コードはほとんどなかったですあなたが思っていたこと。問題は、sapply()の出力がオブジェクトに割り当てられていないため、印刷された後に破棄されることです。

あなたが実際にコードする理由は、Rのスコープルールによるものです。関数に必要なオブジェクトは、引数として関数に渡すべきです。しかし、Rが関数のローカルなオブジェクトを見つけることができない場合、名前に一致するオブジェクトを親環境で検索し、適切な場合はその環境の親を検索し、最終的に地球環境である作業空間に当てます。その結果、コードはxと動作することがありましたが、ただちにコピーされ、sapply()コールの最後に返されたため、コードが機能します。

多くの場合、このコピーには時間とメモリが必要です。これは、人々がRで遅いと思う理由の1つです。forループでオブジェクトを満たす前にオブジェクトの記憶域を割り当てません。記憶域を割り当てない場合、Rはオブジェクトの変更/コピーを行い、ループの次の結果を追加する必要があります。再び

それは環境のコピーが実際には、元のバージョンを指し、環境と例えば、どこでもRで、常にその単純ではない、けれども:あなたはこれらの種類を理解していれば

> a <- new.env() 
> a 
<environment: 0x1af2ee0> 
> b <- 4 
> assign("b", b, env = a) 
> a$b 
[1] 4 
> c <- a ## copy the environment to `c` 
> assign("b", 7, env = c) ## assign something to `b` in env `c` 
> c$b ## as expected 
[1] 7 
> a$b ## also changed `b` in `a` as `a` and `c` are actually the same thing 
[1] 7 

+0

私はこの時間前(朝の英国時間)に書きましたが、サイドトラッキングされていて、送信ボタンをクリックしていない必要があります。 –

0

"グローバル"オブジェクトを関数内から変更したい場合は、非ローカル割り当てを使用することができます。詳細については、R Language Definitionマニュアルを参照してください。

x <- c(1:10) 
# [1] 1 2 3 4 5 6 7 8 9 10 
print(x) 
sapply(1:10,function(i){ 
      x[i] <<- 4 
     }) 
print(x) 
# [1] 4 4 4 4 4 4 4 4 4 4 

あなただけのよりコンパクトx[]<-4、方法によって、Rの素敵な機能の一つである

としてそれを持つことができ、この特定のケースではあるが - forではありません(代わりのsapply(1:10,function(i) x[i] <<- 4またはfor(i in 1:10) x[i]<-4関数なので、ここでは<<-は必要ありません)x[]<-4

関連する問題