Por que duas referências ao mesmo vetor retornam endereços de memória diferentes para cada elemento do vetor?

9

Estou aprendendo R e atualmente estou lendo este livro . Para ter certeza de que entendi o conceito, executei o teste a seguir que acabou sendo bastante confuso para mim e agradeceria se você pudesse esclarecê-lo. Aqui está o teste, que eu executei diretamente no shell R do terminal (não usando o RStudio ou o 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

Você poderia me dizer onde está o meu erro e o que eu entendi errado neste problema?

user17911
fonte
11
Eu não sei R, mas em outros idiomas você tem tipos de valor e referência. Se inteiro for o tipo de valor, como em C ++ ou C #, qualquer atribuição criará um novo número inteiro. Portanto, todo número inteiro terá seu próprio endereço.
Hostel
11
De fato, mesmo executando obj_addr(x[1])duas vezes deve fornecer resultados diferentes, pois cada novo número inteiro terá seu próprio endereço.
Bas
@Como testei o que você mencionou, ou seja, executando sucessivamente obj_addr (x [1]) e, de fato, fazendo isso, R retorna cada vez que um resultado diferente (endereço de memória diferente). Mas não entendo o porquê, porque, como me parece, não atribuo nada, portanto não crio um novo objeto (para o qual obviamente haverá um novo endereço, pois os objetos são imutáveis ​​em R). Para mim, obj_addr (x [1]) significa que estou apenas lendo um objeto já existente.
user17911 7/04

Respostas:

5

Qualquer objeto R é um C (ponteiro SEXP- chamado - para a) "multi-objeto" ( struct). Isso inclui informações (que o R precisa operar, por exemplo length, número de referências - para saber quando copiar um objeto - e mais) sobre o objeto R e também os dados reais do objeto R aos quais temos acesso.

lobstr::obj_addr, presumivelmente, retorna o endereço de memória apontado SEXPpara. Essa parte da memória contém as informações e os dados do objeto R. No ambiente R, não podemos / não precisamos acessar a (ponteiro para) a memória dos dados reais em cada objeto R.

Como Adam observa em sua resposta, a função [ copia o enésimo elemento dos dados contidos no objeto C para um novo objeto C e retorna seu SEXPponteiro para R. Cada vez que [é chamado, um novo objeto C é criado e retornado para R.

Não podemos acessar o endereço de memória de cada elemento dos dados reais de nosso objeto por meio de R. Mas, brincando um pouco, podemos rastrear os respectivos endereços usando a api C:

Uma função para obter os endereços:

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

E aplicando aos nossos dados:

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

A diferença de memória sucessiva entre os elementos de dados do nosso objeto é igual ao tamanho do inttipo:

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

Usando a [função:

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

Essa pode ser uma resposta extensa mais do que a necessária e é simplista quanto aos aspectos técnicos reais, mas, esperançosamente, oferece uma imagem "grande" mais clara.

alexis_laz
fonte
Impressionante! Eu realmente agradeço muito por uma explicação detalhada e clara para pessoas como eu, que são completamente iniciantes em R. Além disso, seu exemplo é muito impressionante ao mostrar a flexibilidade do R e suas possíveis interações poderosas com outras linguagens de programação. Muito obrigado pelo seu tempo e sua ajuda.
user17911 8/04
3

Esta é uma maneira de olhar para isso. Estou certo de que há uma visão mais técnica. Lembre-se que em R, quase tudo é uma função. Isso inclui a função extrair [,. Aqui está uma declaração equivalente a x[1]:

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

Então, o que você está fazendo é executar uma função que retorna um valor (check-out ?Extract). Esse valor é um número inteiro. Quando você executa obj_addr(x[1]), que está avaliando a função x[1]e, em seguida, dando-lhe a obj_addr()de que o retorno de função, não o endereço do primeiro elemento da matriz que você obrigado a ambos xe y.

Adão
fonte
Muito obrigado por sua ajuda e atenção ao meu problema. Na verdade, é isso que eu não sabia, ou seja, recuperar um valor por "Extrair" cria um novo objeto. Como eu disse, sou realmente iniciante no R! Muito obrigado pelo seu tempo e sua descrição.
user17911 8/04