22

Rが関数に引数を渡すとき、変数のコピーを作成するときなど、メモリ使用量に関してRが使用するロジックを理解したいと思います。変数のコピーを実際に作成するのはいつですか?その変数への参照を渡すだけですか?特に私が興味を持っている状況は次のとおりです。

f <- function(x) {x+1}
a <- 1
f(a)

文字通り渡されているのですaか、それとも渡されていることへの参照ですか?

x <- 1
y <- x

コピーの参照?これが当てはまらないのはいつですか?

誰かが私にこれを説明することができれば私は非常にありがたいです。

4

3 に答える 3

17

変数を渡すときは、参照ではなく常にコピーによって行われます。ただし、実際に割り当てが行われるまで、コピーが作成されない場合があります。プロセスの実際の説明は、約束通りです。ドキュメントを見てください

?force
?delayedAssign

実用的な意味の1つは、オブジェクトが通常占有するRAMの少なくとも2倍のRAMが必要になるのを避けることは、不可能ではないにしても非常に難しいということです。大きなオブジェクトを変更するには、通常、一時的なコピーを作成する必要があります。

更新:2015:私はMatt Dowleに同意します(そして同意しました)。彼のdata.tableパッケージは、コピーの複製の問題を回避する割り当てへの代替ルートを提供します。それが要求された更新である場合、提案が行われた時点で私はそれを理解していませんでした。

applyおよびの評価ルールのR3.2.1に最近変更がありましたReduce。ここのニュースを参照してSO発表されました:lapplyから無名関数を返す-何が問題になっていますか?

そして、コメントでjhetzelによって引用された興味深い論文がここにあります

于 2012-05-18T15:41:30.360 に答える
9

遅い答えですが、Web(または少なくとも通常のソース)で十分なカバレッジが得られない言語設計の非常に重要な側面です。

x <- c(0,4,2)
lobstr::obj_addr(x)
# [1] "0x7ff25e82b0f8"
y <- x
lobstr::obj_addr(y)
# [1] "0x7ff25e82b0f8"

同一の「メモリアドレス」、つまりオブジェクトが保存されているメモリ内の場所に注意してください。したがって、それを確認しxy両方が同じ識別子を指していることを確認できます。

ハドリーウィッカムのアドバンストRの本はこれに触れています:

このコードを検討してください

x <- c(1, 2, 3)

「値1、2、および3を含む「x」という名前のオブジェクトを作成する」と読みやすくなります。残念ながら、これは単純化されたものであり、Rが実際に舞台裏で行っていることについての不正確な予測につながります。このコードは2つのことを行っていると言った方が正確です。

オブジェクト、値のベクトル、を作成していますc(1, 2, 3)。そして、そのオブジェクトを名前にバインドしますx。つまり、オブジェクトまたは値には名前がありません。実際には、値を持つ名前です。

これらはメモリアドレスであり、一時的なものであり、新しいRセッションごとに変更されることに注意してください。

ここが重要な部分です。

Rセマンティクスでは、オブジェクトは値によってコピーされます。これは、コピーを変更すると、元のオブジェクトがそのまま残ることを意味します。メモリ内のデータのコピーはコストのかかる操作であるため、R内のコピーは可能な限り遅延します。これらは、新しいオブジェクトが実際に変更されたときにのみ発生します。出典:[Rlangドキュメント][1]

したがってy、ベクトルに値を追加しての値を変更するyと、は別の「オブジェクト」を指すようになります。これは、「新しいオブジェクトが変更された場合にのみ」(遅延)発生するコピー操作に関してドキュメントに記載されている内容と一致します。y以前とは異なるアドレスを指しています。

y <- c(y, -3)
print(lobstr::obj_addr(y))
# [1] "0x7ff25e825b48"
于 2019-10-15T10:29:12.937 に答える
0

@onlyphantomそれはとても役に立ちます!また、オブジェクトはコピーされることなく関数からラウンドトリップすることができます。これが私をここに連れてきた理由です。

tmp <- function(x, create) {
    if(!create){
        x
    }else{
        "new"
    }
}

x = c(0,4,2)

y = tmp(x, F)
lobstr::obj_addr(x) == lobstr::obj_addr(y) # y points to x!

そして、これはxをそれ自体で「置き換える」ときに機能します-コピーはありません!

oldAddr = lobstr::obj_addr(x) 
x = tmp(x, F)
lobstr::obj_addr(x) == oldAddr # TRUE!

マニュアルからのこの例(わずかに変更)も、遅延評価のトリガーについて有益でした

tmp = function(x, label = deparse(x), force=TRUE) {
    if(force){
        label
    }
    x <- x + 1
    print(label); return(x)
}
tmp(2)
[1] "2"
[1] 3

tmp(2, force=F)
[1] "3"
[1] 3

Rバージョン3.6.3

于 2020-03-31T04:31:52.043 に答える