我正在编写一个带有 S4 类的包,它看起来像一个简单的树结构:该类,比如说
S4node
,有一个槽 nodes
,它是子 S4node
的列表(通过一个共同的虚拟祖先,比如说 S4base
)。该类还有一个类型为 parent
的插槽 S4base
,它链接回子节点的父节点。看起来或多或少像这样(为了清晰起见,使用了准系统编码):
# Create the root node, slot parent is set to NA, slot id is set to 567
> root <- new_node(567)
# Create some child nodes, slot parent is set to root, slot nodes has new node added
> root <- add_node(root, 5671)
> root <- add_node(root, 5672)
工作正常,所有插槽均设置正确。向下遍历层次结构和向上遍历层次结构都有效。但问题是:当我想向上遍历层次结构时,
nodes
插槽设置为NULL
,这在向下移动其他分支时变得很明显。像这样:
> node5671 <- find_node(root, 5671)
# Now traverse 5671 -> 567 -> 5672
> node5672 <- node_travel(node5671, "../5672")
Error: path not found
在控制台上,这变得更加清晰:
> length(root@nodes)
[1] 2
> node5671@parent@id
[1] 567
> nodes5671@parent@nodes
NULL
# Best one yet: take the root node, get a child, get the parent (= root), get the nodes = NULL!!!
> root@nodes[[1]]@parent@nodes
NULL
在我看来,带有节点的列表会自动清空,但只有在隐式访问时:我可以首先构造列表,只有当通过
@parent
查找引用列表时,列表才会被清空。这是为了避免无限递归吗?这是gc()
的怪癖吗?有解决方法吗?
我只能假设
add_node
修改其参数的 copy的
nodes
槽并返回修改后的 copy。 副本获得一个新节点,并保留原始节点。 使用 tracemem
... 设计一个示例并不难
> setClass("S4base")
> setClass("S4root",
+ contains = "S4base",
+ slots = c(id = "integer", parent = "NULL", nodes = "list"),
+ prototype = list(id = 0L, parent = NULL, nodes = list()))
> setClass("S4node",
+ contains = "S4base",
+ slots = c(id = "integer", parent = "S4base", nodes = "list"),
+ prototype = list(id = 1L, parent = new("S4root"), nodes = list()))
>
> root <- new("S4root", id = 0L); tracemem(root)
[1] "<0x1370506d8>"
> node <- new("S4node", id = 1L, parent = root)
> root@nodes <- list(node)
tracemem[0x1370506d8 -> 0x1370f5aa8]:
> identical(root, root@nodes[[1L]]@parent)
[1] FALSE
>
> str(root)
Formal class 'S4root' [package ".GlobalEnv"] with 3 slots
..@ id : int 0
..@ parent: NULL
..@ nodes :List of 1
.. ..$ :Formal class 'S4node' [package ".GlobalEnv"] with 3 slots
.. .. .. ..@ id : int 1
.. .. .. ..@ parent:Formal class 'S4root' [package ".GlobalEnv"] with 3 slots
.. .. .. .. .. ..@ id : int 0
.. .. .. .. .. ..@ parent: NULL
.. .. .. .. .. ..@ nodes : list()
.. .. .. ..@ nodes : list()
>
要解决 R 中的修改时复制问题,您可能应该使用环境(可能是扩展
environment
的 S4 类):
> root <- new.env(parent = emptyenv()); try(tracemem(root))
Error in tracemem(root) :
'tracemem' is not useful for promise and environment objects
> node <- new.env(parent = emptyenv())
>
> root$id <- 0L
> root$nodes <- list(node)
>
> node$id <- 1L
> node$nodes <- list()
> node$parent <- root
>
> identical(root, root$nodes[[1L]]$parent)
[1] TRUE
>