Чому дві посилання на один і той же вектор повертають різні адреси пам'яті для кожного елемента вектора?


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])двічі має дати різні результати, оскільки кожне нове ціле число матиме власну адресу.
Бас

@Якже я перевіряв те, що ви згадали, тобто послідовно запускаючи obj_addr (x [1]), і, дійсно, роблячи це, R щоразу повертає різний результат (різну адресу пам'яті). Але я не розумію чому, тому що, як мені здається, я нічого не присвоюю, тому я не створюю нового об'єкта (для якого, очевидно, буде нова адреса, оскільки об'єкти незмінні в R). Для мене obj_addr (x [1]) означає, що я просто читаю вже існуючий об’єкт.
користувач17911

Відповіді:


5

Будь-який R-об'єкт - це C (вказівник, який називається SEXP- на a) "багатооб'єкт" ( struct). Сюди входить інформація (якою R потрібно оперувати, наприклад length, кількість посилань - щоб знати, коли копіювати об'єкт - і багато іншого) про R-об'єкт, а також фактичні дані R-об'єкта, до якого ми маємо доступ.

lobstr::obj_addr, імовірно, повертає адресу пам'яті, на яку SEXPвказує. Ця частина пам'яті містить як інформацію про, так і дані об'єкта R. Зсередини R не можемо / не потребуємо доступу до (вказівника на) пам'яті фактичних даних у кожному R-об'єкті.

Як зазначає Адам у своїй відповіді, функція [ копіює n-й елемент даних, що містяться в об'єкті C, на новий об'єкт 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 та його можливі потужні взаємодії з іншими мовами програмування. Велике спасибі за ваш час та вашу допомогу.
користувач17911

3

Це один із способів поглянути на це. Я впевнений, що є більш технічний вигляд. Пам'ятайте, що в R майже все є функцією. Це включає в себе функцію екстракту, [. Ось рівнозначне твердження x[1]:

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

Отже, що ви робите, це запуск функції, яка повертає значення (перевірити ?Extract). Це значення є цілим числом. Коли ви запускаєте obj_addr(x[1]), він оцінює функцію, x[1]а потім дає вам obj_addr()повернення цієї функції, а не адресу першого елемента масиву, який ви прив’язали до обох xі y.


Дуже дякую за вашу допомогу та вашу увагу до моєї проблеми. Дійсно, це те, чого я не знав, тобто повернення значення за допомогою "Витягування" справді створює новий об'єкт. Як я вже сказав, я справді початківець у R! Дуже дякую за ваш час та опис.
користувач17911
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.