【问题标题】:Copy On Modify; What Happens When You Run This Code? x <- list(1:10); x[[2]] <- x修改时复制;运行此代码时会发生什么? x <- 列表(1:10); x[[2]] <- x
【发布时间】:2018-08-07 19:15:37
【问题描述】:

我对“高级 R”(https://adv-r.hadley.nz/names-values.html)第 3.36 节中的问题 4 有点困惑。我链接到的章节解释了我在附加的图像中使用的约定。我将简要总结一下。对于那些不熟悉这本书的人和那些不想点击链接并阅读作者对与我相似的图表的描述的人,我在下面的图片中添加了对我使用的约定的简要说明这篇文章。

当您运行此代码时会发生什么?画一幅画

x <- list(1:10)
x[[2]] <- x

我了解这里正在创建什么。 x &lt;- list(1:10) 创建一个列表。该列表有一个元素指向向量 1:10。 x[[2]] &lt;- x 运行后,x 指向一个包含两个元素的列表。第一个元素指向一个向量 1:10。第二个元素是对与原始列表相同的列表的引用(或者它可能不仅与原始列表相同,而且实际上是原始列表?)。我只是对新列表中正在复制和/或引用哪些对象(不是变量、对象)感到困惑。

这是一个解决方案的尝试。

在刚刚运行x &lt;- list(1:10) 之后,名称 x 被绑定到一个列表对象。该列表有一个引用,即向量 (1:10)。

这就是我感到困惑的地方。我不确定执行x[[2]] &lt;- x 时会发生什么。这是我最好的猜测,我认为这是错误的。当x[[2]] &lt;- x 被执行时,x 最初绑定到的对象的副本被制作。对于该副本,将创建指向原始对象的第二个元素。

我假设我对x[[2]] &lt;- x 执行时发生的情况的解释是不正确的。如果是这样,有人可以帮助我了解发生了什么吗?如果我碰巧是正确的,有人可以帮助解释为什么我是正确的吗?


在我使用的图像中,圆角正方形代表名称/变量。黑色箭头是从名称到对象的绑定(与 R 中的赋值箭头指向的方向相反)。下面的矩形代表里面有绿色圆圈时的列表。绿色圆圈代表列表的一个元素。由于列表元素是对对象的引用,因此绿色箭头指向列表元素所引用的对象。

在第一张图片中,列表有一个元素,该元素指向向量 1:10。

在第二张图片中,列表对象的第一个元素指向向量 1:10。第二个元素是对列表的引用。

【问题讨论】:

    标签: r list copy


    【解决方案1】:

    也许这会有所帮助。这里我们使用pryr::address 来查看存储对象的内存位置(注意,您的实际地址可能会有所不同,但是当我有匹配的地址时,您的地址也应该匹配)。

    library(pryr)
    x <- list(1:10)
    pryr::address(x)
    # [1] "0x3452810"
    y <- x[[1]]
    pryr::address(y)
    # [1] "0x16b53bf0"
    

    所以我们在给定位置有一个列表x。我们可以将 R 中的列表视为指向其他对象的指针的集合。我们不能直接获取它存储第一项的地址(至少,我不知道如何使用address),但我们可以将该值存储到y,因为R只会在对象时更改地址被修改,我们可以假设这是存储第一个值的地方。现在让我们更新x

    x[[2]] <- x
    pryr::address(x)
    # [1] "0x16001018"
    

    我们可以看到x已经改变并被赋予了一个新的内存位置

    y <- x[[1]]
    pryr::address(y)
    # [1] "0x16b53bf0"
    

    请注意,第一个元素仍然位于相同的内存地址。因此,尚未制作此向量的新副本。新列表只是指向同一个向量。现在我们看一下我们刚刚添加的值的地址

    y <- x[[2]]
    pryr::address(y)
    # [1] "0x3452810"
    

    请注意,此值现在指向原始 x 所在的旧内存地址。

    还有更多

    y <- x[[2]][[1]]
    pryr::address(y)
    # [1] "0x16b53bf0"
    

    两个列表都指向同一个 1:10 向量。它只存储一次。

    因此,当您执行x[[2]]&lt;-x 时,您所做的是创建一个新列表。这个新列表本质上包含两个“指针”。一个指向与原始列表中相同的向量,另一个指向列表的原始地址。

    【讨论】:

    • 这是一个很好的答案!为了将其与 OP 的图表相匹配,我认为图片是正确的。有三个地址,原始向量、原始列表和新列表。新列表已创建,因为它已由 x[[2]] &lt;- x 修改,根据 copy-on-modify。
    • 很好的解释,弗里克先生!非常感谢!我的图表有什么我应该改变的吗?我觉得和你的解释是一致的。
    • 哦,如果您不想重新绑定y &lt;- x[[1]] 等以使用pryr::addressbase::tracemem(x[[1]]) 直接工作并显示相同的结果
    【解决方案2】:

    x &lt;- list(1:10) 在那里你创建了一个列表。让我们使用str()检查它

    > str(x)
    List of 1
     $ : int [1:10] 1 2 3 4 5 6 7 8 9 10
    

    然后您将此列表分配给列表中的下一个位置。如x[[2]] &lt;- x。现在列表x 包含:

    > str(x)
    List of 2
     $ : int [1:10] 1 2 3 4 5 6 7 8 9 10
     $ :List of 1
      ..$ : int [1:10] 1 2 3 4 5 6 7 8 9 10
    

    检查发生了什么。我检查xx[[2]]的类型

    > typeof(x) 
    [1] "list"
    > typeof(x[[2]])
    [1] "list"
    

    因此,x 是一个包含列表的列表。

    更新

    我使用了 lobstr 包中的 ref() 函数。

    > x <- list(1:10)
    > ref(x)
    o <1:0x344642b8> [list]
    \-<2:0x20ad2588> [int]
    
    > x[[2]] <- x
    > ref(x)
    o <1:0x34aee7e0> [list]
    +-<2:0x20ad2588> [int]
    \-o <3:0x344642b8> [list]
      \-<2:0x20ad2588> [int]
    

    结果显示该值对应同一个内存位置。

    【讨论】:

    • 感谢您的回复。你的答案不是我真正想要的。我明白输出是什么。我真正在寻找的是“幕后”发生的事情。是否正在制作原始列表的副本......新列表元素指向哪些对象(不是变量,而是存储在内存中的对象)等等。再次感谢您的回复。我重读了我原来的帖子,我不怪你误解了我的要求。我原来的帖子不是很清楚。我会修改它。
    • 我了解您想要实现的目标。我认为R 复制列表中的所有元素并将它们分配给其他位置的新列表,但我可能是错的。我想知道发生了什么。
    • 我认为你说的基本正确。我认为只是复制了引用,而不是引用指向的实际对象(列表元素是对对象的引用)。来自高级 R:l1
    • 在上面的示例中,l2
    猜你喜欢
    • 2016-02-04
    • 2022-12-15
    • 2021-10-14
    • 1970-01-01
    • 2016-12-25
    • 1970-01-01
    • 1970-01-01
    • 2020-01-13
    • 2021-01-19
    相关资源
    最近更新 更多