Warum geben zwei Verweise auf denselben Vektor unterschiedliche Speicheradressen für jedes Element des Vektors zurück?

9

Ich lerne R und lese gerade dieses Buch . Um sicherzustellen, dass ich das Konzept verstehe, habe ich den folgenden Test durchgeführt, der sich für mich als ziemlich verwirrend herausstellte, und ich würde mich freuen, wenn Sie ihn klären könnten. Hier ist der Test, den ich direkt in der R-Shell vom Terminal aus ausgeführt habe (ohne RStudio oder 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

Können Sie mir sagen, wo mein Fehler liegt und was ich bei diesem Problem falsch verstanden habe?

user17911
quelle
1
Ich kenne R nicht, aber in anderen Sprachen haben Sie Wert- und Referenztypen. Wenn die Ganzzahl ein Werttyp wie in C ++ oder C # ist, wird bei jeder Zuweisung eine neue Ganzzahl erstellt. Jede Ganzzahl hat also eine eigene Adresse.
Hostel
1
Selbst obj_addr(x[1])wenn Sie zweimal ausführen, sollten Sie unterschiedliche Ergebnisse erzielen, da jede neue Ganzzahl eine eigene Adresse hat.
Bas
@Bas ich getestet habe, was Sie erwähnt haben, dh nacheinander obj_addr (x [1]) ausgeführt hat, und tatsächlich gibt R jedes Mal ein anderes Ergebnis zurück (unterschiedliche Speicheradresse). Aber ich verstehe nicht warum, weil ich, wie es mir scheint, nichts zuweise, also erstelle ich kein neues Objekt (für das es offensichtlich eine neue Adresse gibt, da Objekte in R unveränderlich sind). Für mich bedeutet obj_addr (x [1]), dass ich nur ein bereits vorhandenes Objekt lese.
user17911

Antworten:

5

Jedes R-Objekt ist ein C (Zeiger genannt SEXP- auf a) "Multi-Objekt" ( struct). Dies umfasst Informationen (die R lengthausführen muss, z. B. die Anzahl der Referenzen - um zu wissen, wann ein Objekt kopiert werden muss - und mehr) über das R-Objekt und auch die tatsächlichen Daten des R-Objekts, auf das wir Zugriff haben.

lobstr::obj_addrVermutlich gibt die Speicheradresse zurück, auf die a SEXPzeigt. Dieser Teil des Speichers enthält sowohl die Informationen als auch die Daten des R-Objekts. Innerhalb der R-Umgebung können / müssen wir nicht auf den (Zeiger auf den) Speicher der tatsächlichen Daten in jedem R-Objekt zugreifen.

Wie Adam in seiner Antwort feststellt, [ kopiert die Funktion das n-te Element der im C-Objekt enthaltenen Daten in ein neues C-Objekt und gibt seinen SEXPZeiger auf R zurück. Bei jedem [Aufruf wird ein neues C-Objekt erstellt und an R zurückgegeben.

Wir können nicht über R auf die Speicheradresse jedes Elements der tatsächlichen Daten unseres Objekts zugreifen. Wenn wir jedoch ein bisschen herumspielen, können wir die entsprechenden Adressen mit der C api verfolgen:

Eine Funktion zum Abrufen der Adressen:

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

Und auf unsere Daten anwenden:

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

Der sukzessive Speicherunterschied zwischen den Datenelementen unseres Objekts entspricht der Größe des intTyps:

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

Verwenden der [Funktion:

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

Dies könnte eine mehr als notwendige ausführliche Antwort sein und vereinfacht die tatsächlichen technischen Details, bietet aber hoffentlich ein klareres "Gesamtbild".

alexis_laz
quelle
Genial! Ich danke Ihnen sehr für die detaillierte und klare Erklärung für Leute wie mich, die Anfänger in R sind. Außerdem zeigt Ihr Beispiel sehr beeindruckend die Flexibilität von R und seine möglichen leistungsstarken Interaktionen mit anderen Programmiersprachen. Vielen Dank für Ihre Zeit und Ihre Hilfe.
user17911
3

Dies ist eine Möglichkeit, es zu betrachten. Ich bin sicher, dass es eine technischere Sichtweise gibt. Denken Sie daran, dass in R fast alles eine Funktion ist. Dies beinhaltet die Extraktionsfunktion , [. Hier ist eine äquivalente Aussage zu x[1]:

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

Sie führen also eine Funktion aus, die einen Wert zurückgibt (Auschecken ?Extract). Dieser Wert ist eine Ganzzahl. Wenn Sie ausführen obj_addr(x[1]), wird die Funktion ausgewertet x[1]und Sie erhalten dann die obj_addr()Rückgabe dieser Funktion, nicht die Adresse des ersten Elements des Arrays, das Sie an xund gebunden haben y.

Adam
quelle
Vielen Dank für Ihre Hilfe und Ihre Aufmerksamkeit für mein Problem. In der Tat ist es das, was ich nicht wusste, das heißt, das Abrufen eines Wertes durch "Extrahieren" erzeugt tatsächlich ein neues Objekt. Wie gesagt ich bin wirklich ein Anfänger in R! Vielen Dank für Ihre Zeit und Ihre Beschreibung.
user17911