Mengapa dua referensi ke vektor yang sama mengembalikan alamat memori yang berbeda untuk setiap elemen vektor?

9

Saya belajar R dan saat ini saya sedang membaca buku ini . Untuk memastikan saya memahami konsepnya, saya menjalankan tes berikut yang ternyata cukup membingungkan bagi saya dan saya akan menghargai jika Anda bisa menjelaskannya. Inilah tesnya, yang saya jalankan langsung di shell R dari terminal (tidak menggunakan RStudio atau 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

Bisakah Anda memberi tahu saya di mana kesalahan saya dan apa yang saya salah pahami dalam masalah ini?

pengguna17911
sumber
1
Saya tidak tahu R tetapi dalam bahasa lain Anda memiliki nilai dan tipe referensi. Jika integer adalah tipe nilai seperti dalam C ++ atau C # maka setiap penugasan akan membuat integer baru. Jadi setiap integer akan memiliki alamatnya sendiri.
Hostel
1
Memang, bahkan menjalankan obj_addr(x[1])dua kali akan memberi Anda hasil yang berbeda, karena setiap integer baru akan memiliki alamatnya sendiri.
Bas
@Bas Saya menguji apa yang Anda sebutkan, yaitu, menjalankan berturut-turut obj_addr (x [1]), dan memang demikian, R mengembalikan setiap kali hasil yang berbeda (alamat memori berbeda). Tapi saya tidak mengerti mengapa, karena menurut saya saya tidak menetapkan apa-apa, jadi saya tidak membuat objek baru (yang jelas akan ada alamat baru karena objek tidak dapat diubah dalam R). Bagi saya obj_addr (x [1]) berarti saya hanya membaca objek yang sudah ada.
user17911

Jawaban:

5

Setiap objek R adalah C (pointer -called SEXP- to a) "multi-object" ( struct). Ini termasuk informasi (yang perlu dioperasikan R, mis. length, Jumlah referensi - untuk mengetahui kapan harus menyalin suatu objek - dan lebih banyak lagi) tentang objek R dan, juga, data aktual dari objek R yang kita miliki aksesnya.

lobstr::obj_addr, mungkin, mengembalikan alamat memori yang SEXPditunjuk. Bagian memori itu berisi informasi tentang dan data objek R. Dari dalam lingkungan R kita tidak bisa / tidak perlu mengakses (pointer ke) memori data aktual di setiap objek R.

Seperti yang dicatat Adam dalam jawabannya, fungsi ini [ menyalin elemen ke-n dari data yang terdapat dalam objek C ke objek C baru dan mengembalikan SEXPpenunjuknya ke R. Setiap kali [dipanggil, objek C baru dibuat dan dikembalikan ke R.

Kami tidak dapat mengakses alamat memori dari setiap elemen data aktual objek kami melalui R. Namun dengan sedikit memutar, kami dapat melacak masing-masing alamat menggunakan api C:

Fungsi untuk mendapatkan alamat:

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);
     ')

Dan berlaku untuk data kami:

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

Perbedaan memori berurutan antara elemen data objek kami sama dengan ukuran intjenis:

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

Menggunakan [fungsi:

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

Ini mungkin jawaban yang lebih luas dari yang diperlukan dan sederhana dalam hal teknis sebenarnya, tetapi, semoga, menawarkan gambaran "besar" yang lebih jelas.

alexis_laz
sumber
Luar biasa! Saya benar-benar berterima kasih banyak untuk penjelasan yang begitu rinci dan jelas untuk orang-orang seperti saya yang benar-benar pemula di R. Juga, contoh Anda sangat mengesankan dalam menunjukkan fleksibilitas R dan kemungkinan interaksi yang kuat dengan bahasa pemrograman lain. Terima kasih banyak atas waktu dan bantuan Anda.
user17911
3

Ini adalah salah satu cara untuk melihatnya. Saya yakin ada pandangan yang lebih teknis. Ingatlah bahwa dalam R, hampir semuanya adalah fungsi. Ini termasuk fungsi ekstrak [,. Berikut ini adalah pernyataan yang setara untuk x[1]:

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

Jadi apa yang Anda lakukan adalah menjalankan fungsi yang mengembalikan nilai (periksa ?Extract). Nilai itu adalah bilangan bulat. Ketika Anda menjalankan obj_addr(x[1]), itu mengevaluasi fungsi x[1]dan kemudian memberi Anda obj_addr()kembali fungsi itu, bukan alamat elemen pertama dari array yang terikat ke keduanya xdan y.

Adam
sumber
Terima kasih banyak atas bantuan Anda dan perhatian Anda pada masalah saya. Memang inilah yang saya tidak tahu, yaitu, mengambil kembali nilai dengan "Ekstrak" memang menciptakan objek baru. Seperti yang saya katakan, saya benar-benar pemula di R! Terima kasih banyak atas waktu dan deskripsi Anda.
user17911