ทำไมการอ้างอิงสองรายการไปยังเวกเตอร์เดียวกันจึงส่งกลับที่อยู่หน่วยความจำที่แตกต่างกันสำหรับแต่ละองค์ประกอบของเวกเตอร์


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])สองครั้งก็ควรให้ผลลัพธ์ที่แตกต่างกันเพราะจำนวนเต็มใหม่ทุกอันจะมีที่อยู่ของตัวเอง
Bas

@Bas ฉันทดสอบสิ่งที่คุณพูดถึงนั่นคือการรัน obj_addr (x [1]) อย่างต่อเนื่องและแน่นอนการทำเช่นนั้น R จะคืนค่าผลลัพธ์ที่แตกต่างกันทุกครั้ง (ที่อยู่หน่วยความจำที่แตกต่างกัน) แต่ฉันไม่เข้าใจว่าเพราะเพราะฉันไม่ได้กำหนดอะไรเลยดังนั้นฉันจึงไม่สร้างวัตถุใหม่ (ซึ่งเห็นได้ชัดว่าจะมีที่อยู่ใหม่เมื่อวัตถุไม่เปลี่ยนรูปใน R) สำหรับฉัน obj_addr (x [1]) หมายความว่าฉันแค่อ่านวัตถุที่มีอยู่แล้ว
user17911

คำตอบ:


5

วัตถุ R ใด ๆ คือ C (ตัวชี้SEXP- เรียกว่า- ถึง a) "multi-object" ( struct) ซึ่งรวมถึงข้อมูล (ที่ R จำเป็นต้องทำงานเช่นlengthจำนวนการอ้างอิง - เพื่อทราบว่าเมื่อใดที่จะคัดลอกวัตถุ - และอื่น ๆ ) เกี่ยวกับวัตถุ R และยังข้อมูลที่แท้จริงของวัตถุ R ที่เราสามารถเข้าถึงได้

lobstr::obj_addrสมมุติว่าส่งคืนที่อยู่หน่วยความจำที่SEXPชี้ไป ส่วนหนึ่งของหน่วยความจำนั้นมีทั้งข้อมูลเกี่ยวกับและข้อมูลของวัตถุ R จากสภาพแวดล้อม R เราไม่สามารถ / ไม่จำเป็นต้องเข้าถึงหน่วยความจำ (ตัวชี้ไปยัง) ของข้อมูลจริงในแต่ละวัตถุ R

ในขณะที่อดัมบันทึกคำตอบของเขาฟังก์ชันจะ[ คัดลอกองค์ประกอบที่ n ของข้อมูลที่อยู่ในวัตถุ C ไปยังวัตถุ C ใหม่และคืนค่าSEXPตัวชี้ไปที่อาร์ทุกครั้งที่[มีการเรียกวัตถุ C ใหม่จะถูกสร้างขึ้นและส่งกลับไปยังอาร์

เราไม่สามารถเข้าถึงที่อยู่หน่วยความจำของแต่ละองค์ประกอบของข้อมูลจริงของวัตถุของเราผ่านทาง 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 และการโต้ตอบที่ทรงพลังกับภาษาโปรแกรมอื่น ๆ ขอบคุณมากสำหรับเวลาและความช่วยเหลือของคุณ
user17911

3

นี่เป็นวิธีหนึ่งในการดู ฉันแน่ใจว่ามีมุมมองทางเทคนิคมากขึ้น จำไว้ว่าใน R เกือบทุกอย่างเป็นฟังก์ชั่น ซึ่งรวมถึงฟังก์ชั่นการแยกข้อมูล, [. นี่คือคำสั่งที่เทียบเท่ากับx[1]:

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

ดังนั้นสิ่งที่คุณกำลังทำคือการใช้ฟังก์ชั่นที่คืนค่า (ตรวจสอบ?Extract) ค่านั้นเป็นจำนวนเต็ม เมื่อคุณเรียกใช้obj_addr(x[1])ก็คือการประเมินการทำงานx[1]และจากนั้นให้คุณobj_addr()ของการกลับมาฟังก์ชั่นที่ไม่ใช่ที่อยู่ขององค์ประกอบแรกของอาร์เรย์ที่คุณผูกไว้กับทั้งสองและxy


ขอบคุณมากสำหรับความช่วยเหลือและความสนใจของคุณต่อปัญหาของฉัน อันที่จริงนี่คือสิ่งที่ฉันไม่ทราบนั่นคือการดึงค่าโดย "แยก" สร้างวัตถุใหม่แน่นอน อย่างที่ฉันบอกว่าฉันเป็นมือใหม่ใน R! ขอบคุณมากสำหรับเวลาและคำอธิบายของคุณ
user17911
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.