同じベクターへの2つの参照がベクターの各要素に対して異なるメモリアドレスを返すのはなぜですか?


9

私はRを学び、現在この本を読んでます。概念を確実に理解するために、次のテストを実行しましたが、わかりにくいので、明確にしていただければ幸いです。これは、ターミナルからRシェルで直接実行したテストです(RStudioまたはEmacs ESSを使用していません)。

> library(lobstr)
>
> x <- c(1500,2400,8800)
> y <- x
> ### So the following two lines must return the same memory address
> obj_addr(x)
[1] "0xb23bc50"
> obj_addr(y)
[1] "0xb23bc50"
> ### So as I expected, indeed both x and y point to the same memory 
> ### location: 0xb23bc50
>
>
>
> ### Now let's check that each element can be referenced by the same
> ### memory address either by using x or y
> x[1]
[1] 1500
> y[1]
[1] 1500
> obj_addr(x[1])
[1] "0xc194858"
> obj_addr(y[1])
[1] "0xc17db88"
> ### And here is exactly what I don't understand: x and y point 
> ### to the same memory address, so the same must be true for 
> ### x[1] and y[1]. So how come I obtain two different memory
> ### addresses for the same element of the same vector?
>
>
>
> x[2]
[1] 2400
> y[2]
[1] 2400
> obj_addr(x[2])
[1] "0xc15eca0"
> obj_addr(y[2])
[1] "0xc145d30"
> ### Same problem!
>
>
>
> x[3]
[1] 8800
> y[3]
[1] 8800
> obj_addr(x[3])
[1] "0xc10e9b0"
> obj_addr(y[3])
[1] "0xc0f78e8"
> ### Again the same problem: different memory addresses

私の間違いがどこにあるのか、この問題で私が誤解していることを教えていただけませんか?


1
私はRを知りませんが、他の言語では値と参照型があります。整数がC ++またはC#の場合のように値型の場合、割り当てによって新しい整数が作成されます。したがって、すべての整数には独自のアドレスがあります。
ホステル

1
実際、obj_addr(x[1])新しい整数はすべて独自のアドレスを持つため、2回実行しても結果が異なるはずです。
バス

@Bas私はあなたが言ったことをテストしました。つまり、obj_addr(x [1])を連続して実行しました。実際、そうすることで、Rは毎回異なる結果(異なるメモリアドレス)を返します。しかし、理由はわかりません。私には何も割り当てていないようで、新しいオブジェクトを作成していません(Rでオブジェクトが不変であるため、新しいオブジェクトが存在することは明らかです)。私にとってobj_addr(x [1])は、既存のオブジェクトを読み取っているだけであることを意味します。
user17911

回答:


5

すべてのRオブジェクトはC(a SEXPへのポインター-と呼ばれます)の「マルチオブジェクト」(struct)です。これにはlength、Rオブジェクトに関する情報(Rが操作する必要がある、たとえば、オブジェクトのコピー時期を知るための参照数など)と、アクセスできるRオブジェクトの実際のデータも含まれます。

lobstr::obj_addrは、おそらく、aがSEXP指すメモリアドレスを返します。メモリのその部分には、Rオブジェクトの情報データの両方が含まれています。R環境内からは、各Rオブジェクトの実際のデータの(へのポインター)メモリにアクセスする必要はありません。

Adamが回答に記したように、関数はCオブジェクトに含まれるデータのn番目の要素を新しいCオブジェクトに[ コピーし、そのSEXPポインターをRに返します。[呼び出されるたびに、新しいCオブジェクトが作成され、Rに返されます。

Rを介してオブジェクトの実際のデータの各要素のメモリアドレスにアクセスすることはできません。少し遊んで、C apiを使用してそれぞれのアドレスをトレースできます。

アドレスを取得する関数:

ff = inline::cfunction(sig = c(x = "integer"), body = '
             Rprintf("SEXP @ %p\\n", x);

             Rprintf("first element of SEXP actual data @ %p\\n", INTEGER(x));

             for(int i = 0; i < LENGTH(x); i++) 
                 Rprintf("<%d> @ %p\\n", INTEGER(x)[i], INTEGER(x) + i);

             return(R_NilValue);
     ')

そして、私たちのデータに適用します:

x = c(1500L, 2400L, 8800L)  #converted to "integer" for convenience
y = x

lobstr::obj_addr(x)
#[1] "0x1d1c0598"
lobstr::obj_addr(y)
#[1] "0x1d1c0598"

ff(x)
#SEXP @ 0x1d1c0598
#first element of SEXP actual data @ 0x1d1c05c8
#<1500> @ 0x1d1c05c8
#<2400> @ 0x1d1c05cc
#<8800> @ 0x1d1c05d0
#NULL
ff(y)
#SEXP @ 0x1d1c0598
#first element of SEXP actual data @ 0x1d1c05c8
#<1500> @ 0x1d1c05c8
#<2400> @ 0x1d1c05cc
#<8800> @ 0x1d1c05d0
#NULL

オブジェクトのデータ要素間の連続するメモリの違いは、intタイプのサイズに等しくなります。

diff(c(strtoi("0x1d1c05c8", 16), 
       strtoi("0x1d1c05cc", 16), 
       strtoi("0x1d1c05d0", 16)))
#[1] 4 4

[関数の使用:

ff(x[1])
#SEXP @ 0x22998358
#first element of SEXP actual data @ 0x22998388
#<1500> @ 0x22998388
#NULL
ff(x[1])
#SEXP @ 0x22998438
#first element of SEXP actual data @ 0x22998468
#<1500> @ 0x22998468
#NULL

これは必要以上の広範な回答である可能性があり、実際の技術的に単純化していますが、うまくいけば、より明確な「全体像」を提供します。


驚くばかり!完全にRの初心者である私のような人々に対して、このような詳細で明確な説明を本当にありがとうございました。また、Rの柔軟性と他のプログラミング言語との強力な相互作用の可能性を示す点で、あなたの例は非常に印象的です。あなたの時間とあなたの助けに感謝します。
user17911

3

これはそれを見る1つの方法です。もっと技術的な見方があると思います。Rでは、ほとんどすべてが関数であることに注意してください。これには、抽出関数が含まれます[。これと同等のステートメントはx[1]次のとおりです。

> `[`(x, 1)
[1] 1500

つまり、値を返す関数を実行しています(チェックアウト?Extract)。その値は整数です。あなたが実行するとobj_addr(x[1])、それは関数を評価されx[1]、その後、あなたを与えてobj_addr()、その関数の戻り値ではなく、あなたが両方に結合したことを、配列の最初の要素のアドレスではxy


あなたの助けと私の問題への注意をありがとうございました。実際、これは私が知らなかったものです。つまり、「抽出」によって値を取得すると、実際に新しいオブジェクトが作成されます。私が言ったように、私は本当にRの初心者です!お時間と説明をありがとうございました。
user17911
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.