Tại sao hai tham chiếu đến cùng một vectơ trả về các địa chỉ bộ nhớ khác nhau cho mỗi phần tử của vectơ?


9

Tôi đang học R và hiện tôi đang đọc cuốn sách này . Để chắc chắn rằng tôi hiểu khái niệm này, tôi đã chạy thử nghiệm sau đây hóa ra khá khó hiểu đối với tôi và tôi đánh giá cao nếu bạn có thể làm rõ nó. Đây là bài kiểm tra mà tôi đã chạy trực tiếp trong trình bao R từ thiết bị đầu cuối (không sử dụng RStudio hoặc 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

Bạn có thể cho tôi biết lỗi của tôi ở đâu và những gì tôi đã hiểu sai trong vấn đề này?


1
Tôi không biết R nhưng trong các ngôn ngữ khác, bạn có các loại tham chiếu và giá trị. Nếu số nguyên là loại giá trị như trong C ++ hoặc C # thì bất kỳ phép gán nào cũng sẽ tạo số nguyên mới. Vì vậy, mỗi số nguyên sẽ có địa chỉ riêng.
Nhà trọ

1
Thật vậy, thậm chí chạy obj_addr(x[1])hai lần sẽ cho bạn kết quả khác nhau, vì mỗi số nguyên mới sẽ có địa chỉ riêng.
Bas

@Bas Tôi đã kiểm tra những gì bạn đã đề cập, nghĩa là chạy liên tiếp obj_addr (x [1]) và thực sự làm như vậy, R trả về mỗi lần một kết quả khác nhau (địa chỉ bộ nhớ khác nhau). Nhưng tôi không hiểu tại sao, vì dường như đối với tôi, tôi không chỉ định bất cứ điều gì, vì vậy tôi không tạo ra một đối tượng mới (mà rõ ràng sẽ có một địa chỉ mới vì các đối tượng là bất biến trong R). Đối với tôi obj_addr (x [1]) có nghĩa là tôi chỉ đọc một đối tượng đã tồn tại.
dùng17911

Câu trả lời:


5

Bất kỳ đối tượng R nào cũng là C (con trỏ được gọi SEXP- đến a) "đa đối tượng" ( struct). Điều này bao gồm thông tin (R cần phải hoạt động, ví dụ: lengthsố lượng tài liệu tham khảo - để biết khi nào cần sao chép một đối tượng - và hơn thế nữa) về đối tượng R và, cũng như dữ liệu thực tế của đối tượng R mà chúng ta có quyền truy cập.

lobstr::obj_addr, có lẽ, trả về địa chỉ bộ nhớ mà một SEXPđiểm. Phần bộ nhớ đó chứa cả thông tin về dữ liệu của đối tượng R. Từ trong môi trường R, chúng ta không thể / không cần truy cập vào bộ nhớ (con trỏ tới) của dữ liệu thực tế trong mỗi đối tượng R.

Như Adam lưu ý trong câu trả lời của mình, hàm sẽ [ sao chép phần tử thứ n của dữ liệu chứa trong đối tượng C sang đối tượng C mới và trả về SEXPcon trỏ của nó cho R. Mỗi lần [được gọi, một đối tượng C mới được tạo và trả về R.

Chúng tôi không thể truy cập địa chỉ bộ nhớ của từng thành phần của dữ liệu thực tế của đối tượng thông qua R. Nhưng khi chơi một chút, chúng tôi có thể theo dõi các địa chỉ tương ứng bằng cách sử dụng C api:

Một chức năng để có được địa chỉ:

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

Và áp dụng cho dữ liệu của chúng tôi:

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

Sự khác biệt bộ nhớ liên tiếp giữa các thành phần dữ liệu của đối tượng của chúng tôi bằng kích thước của intloại:

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

Sử dụng [chức năng:

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

Đây có thể là một câu trả lời rộng rãi hơn mức cần thiết và đơn giản về các kỹ thuật thực tế, nhưng, hy vọng, cung cấp một bức tranh "lớn" rõ ràng hơn.


Tuyệt vời! Tôi thực sự cảm ơn bạn rất nhiều vì lời giải thích chi tiết và rõ ràng như vậy cho những người như tôi hoàn toàn mới bắt đầu ở R. Ngoài ra, ví dụ của bạn rất ấn tượng trong việc thể hiện tính linh hoạt của R và khả năng tương tác mạnh mẽ của nó với các ngôn ngữ lập trình khác. Rất cám ơn cho thời gian và sự giúp đỡ của bạn.
dùng17911

3

Đây là một cách để xem xét nó. Tôi chắc chắn có một cái nhìn kỹ thuật hơn. Hãy nhớ rằng trong R, gần như mọi thứ là một hàm. Điều này bao gồm chức năng giải nén , [. Đây là một tuyên bố tương đương với x[1]:

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

Vì vậy, những gì bạn đang làm là chạy một hàm trả về một giá trị (kiểm tra ?Extract). Giá trị đó là một số nguyên. Khi bạn chạy obj_addr(x[1]), nó đang đánh giá hàm x[1]và sau đó cung cấp cho bạn obj_addr()hàm trả về của hàm đó, không phải là địa chỉ của phần tử đầu tiên của mảng mà bạn liên kết với cả hai xy.


Cảm ơn bạn rất nhiều vì sự giúp đỡ của bạn và sự quan tâm của bạn đến vấn đề của tôi. Quả thực đây là điều tôi không biết, nghĩa là, lấy lại một giá trị bằng cách "Trích xuất" thực sự tạo ra một đối tượng mới. Như tôi đã nói tôi thực sự là người mới bắt đầu học R! Cảm ơn bạn rất nhiều vì thời gian và mô tả của bạn.
dùng17911
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.