Neden aynı vektöre yapılan iki referans vektörün her bir elemanı için farklı bellek adresleri döndürür?


9

R öğreniyorum ve şu anda bu kitabı okuyorum . Kavramı anladığımdan emin olmak için, benim için oldukça kafa karıştırıcı olduğu ortaya çıkan aşağıdaki testi çalıştırdım ve açıklığa kavuşturabileceğinizi takdir ediyorum. İşte doğrudan terminalden R kabuğunda koştuğum test (RStudio veya Emacs ESS kullanmadan).

> 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

Bana hatanın nerede olduğunu ve bu problemde yanlış anladığımı söyleyebilir misiniz?


1
R bilmiyorum ama diğer dillerde değer ve referans türleriniz var. Tamsayı C ++ veya C # 'da olduğu gibi değer türüyse, herhangi bir atama yeni tamsayı oluşturur. Yani her tamsayının kendi adresi olacaktır.
Hostel

1
Aslında, obj_addr(x[1])her yeni tamsayı kendine ait bir adrese sahip olacağından, iki kez koşmak bile size farklı sonuçlar vermelidir.
Bas

@Ben söylediklerinizi test ettim, yani art arda obj_addr (x [1]) çalıştırıyorum ve bunu yaptığımda, R her seferinde farklı bir sonuç (farklı bellek adresi) döndürüyor. Ama nedenini anlamıyorum, çünkü bana göre hiçbir şey atamıyorum, bu yüzden yeni bir nesne oluşturmuyorum (bunun için nesneler R'de değişmez olduğu için yeni bir adres olacak). Benim için obj_addr (x [1]) zaten var olan bir nesneyi okuduğum anlamına geliyor.
user17911

Yanıtlar:


5

Herhangi bir R nesnesi bir C'dir (işaretçi çağrılır SEXP- a) "çok nesneli " ( struct). Bu, lengthR nesnesi hakkında bilgi (örneğin , bir nesnenin ne zaman kopyalanacağını bilmek için referans sayısı ve daha fazlası) ve ayrıca eriştiğimiz R nesnesinin gerçek verilerini içerir.

lobstr::obj_addr, muhtemelen SEXPişaret ettiği bellek adresini döndürür . Bellek bu kısmı hakkında bilgi her ikisini de içerir ve R 'objenin verilerinin. R ortamından, her bir R nesnesindeki gerçek verilerin belleğine (işaretçi) erişemeyiz / ihtiyacımız yoktur.

Adam cevabında belirttiği gibi, fonksiyon C nesnesindeki verilerin n. Elemanını yeni bir C nesnesine [ kopyalar ve SEXPişaretçisini R'ye döndürür . Her [çağrıldığında, yeni bir C nesnesi oluşturulur ve R'ye geri döner .

Nesnemizin gerçek verilerinin her bir elemanının bellek adresine R aracılığıyla erişemeyiz. Ancak biraz oynatırken, C api'yi kullanarak ilgili adresleri izleyebiliriz:

Adresleri almak için bir fonksiyon:

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

Ve verilerimize başvurmak:

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

Nesnemizin veri öğeleri arasındaki ardışık bellek farkı inttürün boyutuna eşittir :

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

[Fonksiyonunu kullanarak :

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

Bu, ihtiyaç duyulandan daha kapsamlı bir cevap olabilir ve gerçek teknikler üzerinde basittir, ancak umarım daha net bir "büyük" resim sunar.


Müthiş! R gibi tamamen yeni başlayanlar gibi benim gibi insanlar için bu kadar ayrıntılı ve net bir açıklama için gerçekten çok teşekkür ederim. Zaman ayırdığınız ve yardımlarınız için çok teşekkürler.
user17911

3

Buna bakmanın bir yolu bu. Eminim daha teknik bir görüş vardır. R'de neredeyse her şeyin bir işlev olduğunu unutmayın. Buna çıkarma işlevi de dahildir [. İşte buna eşdeğer bir ifade x[1]:

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

Yaptığınız şey, bir değer döndüren bir işlevi çalıştırmaktır (kullanıma alma ?Extract). Bu değer bir tamsayıdır. Eğer çalıştırdığınızda obj_addr(x[1]), bu işlevi değerlendirdiği x[1]ve sonra size vererek obj_addr()hem bağlı olduğu dizinin ilk elemanının bu fonksiyon geri dönüş değil, adresini xve y.


Yardımın ve sorunuma gösterdiğiniz ilgi için çok teşekkür ederim. Aslında bilmediğim şey budur, yani “Extract” ile bir değeri almak gerçekten yeni bir nesne yaratır. Dediğim gibi ben gerçekten R acemi! Zaman ayırdığınız ve açıkladığınız için çok teşekkür ederim.
user17911
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.