Pourquoi deux références au même vecteur renvoient-elles des adresses mémoire différentes pour chaque élément du vecteur?

9

J'apprends R et actuellement je lis ce livre . Pour m'assurer de bien comprendre le concept, j'ai effectué le test suivant qui s'est avéré assez déroutant pour moi et j'apprécierais si vous pouviez le clarifier. Voici le test, que j'ai exécuté directement dans le shell R à partir du terminal (sans utiliser RStudio ou 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

Pourriez-vous me dire où est mon erreur et ce que j'ai mal compris dans ce problème?

user17911
la source
1
Je ne connais pas R mais dans d'autres langues, vous avez des types de valeur et de référence. Si entier est un type de valeur comme en C ++ ou C #, alors toute affectation créera un nouvel entier. Ainsi, chaque entier aura sa propre adresse.
auberge de jeunesse le
1
En effet, même courir obj_addr(x[1])deux fois devrait vous donner des résultats différents, car chaque nouvel entier aura sa propre adresse.
Bas
@Bas j'ai testé ce que vous avez mentionné, c'est-à-dire en exécutant successivement obj_addr (x [1]), et en effet, R retourne chaque fois un résultat différent (adresse mémoire différente). Mais je ne comprends pas pourquoi, car il me semble que je n'attribue rien, donc je ne crée pas de nouvel objet (pour lequel il y aura évidemment une nouvelle adresse car les objets sont immuables dans R). Pour moi, obj_addr (x [1]) signifie que je ne fais que lire un objet déjà existant.
user17911

Réponses:

5

Tout objet R est un C (pointeur -appelé SEXP- vers a) "multi-objet" ( struct). Cela inclut des informations (que R doit exploiter, par exemple length, le nombre de références - pour savoir quand copier un objet - et plus) sur l'objet R et, également, les données réelles de l'objet R auxquelles nous avons accès.

lobstr::obj_addr, vraisemblablement, renvoie l'adresse mémoire vers laquelle SEXPpointe un . Cette partie de la mémoire contient à la fois les informations et les données de l'objet R. Depuis l'environnement R, nous ne pouvons / n'avons pas besoin d'accéder à la (pointeur vers) la mémoire des données réelles de chaque objet R.

Comme Adam le note dans sa réponse, la fonction [ copie le nième élément des données contenues dans l'objet C dans un nouvel objet C et renvoie son SEXPpointeur sur R. À chaque [appel, un nouvel objet C est créé et renvoyé à R.

Nous ne pouvons pas accéder à l'adresse mémoire de chaque élément des données réelles de notre objet via R. Mais en jouant un peu, nous pouvons tracer les adresses respectives à l'aide de l'API C:

Une fonction pour obtenir les adresses:

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

Et en appliquant à nos données:

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

La différence de mémoire successive entre les éléments de données de notre objet est égale à la taille du inttype:

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

Utilisation de la [fonction:

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

Cela pourrait être une réponse approfondie plus que nécessaire et simpliste sur les aspects techniques réels, mais, espérons-le, offre une "vue d'ensemble" plus claire.

alexis_laz
la source
Impressionnant! Je vous remercie vraiment beaucoup pour des explications aussi détaillées et claires pour des gens comme moi qui sont complètement débutants en R. De plus, votre exemple est très impressionnant en montrant la flexibilité de R et ses possibles interactions puissantes avec d'autres langages de programmation. Merci beaucoup pour votre temps et votre aide.
user17911
3

C'est une façon de voir les choses. Je suis sûr qu'il y a une vision plus technique. N'oubliez pas qu'en R, presque tout est fonction. Cela inclut la fonction d'extraction, [. Voici une déclaration équivalente à x[1]:

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

Donc, ce que vous faites, c'est exécuter une fonction qui retourne une valeur (check out ?Extract). Cette valeur est un entier. Lorsque vous exécutez obj_addr(x[1]), il évalue la fonction x[1]et vous donne ensuite le obj_addr()retour de cette fonction, pas l'adresse du premier élément du tableau que vous avez lié aux deuxx et y.

Adam
la source
Merci beaucoup pour votre aide et votre attention à mon problème. En effet c'est ce que je ne savais pas, c'est-à-dire que retrouver une valeur par "Extraire" crée en effet un nouvel objet. Comme je l'ai dit, je suis vraiment un débutant en R! Merci beaucoup pour votre temps et votre description.
user17911