自引用数据结构

Chi*_*dio 3 haskell

我正在尝试为图表构建数据结构。

以下代码似乎按预期工作:

data NodeRef = NodeRef String Int -- NodeRef name targetIndex

data Node = Node String Node -- Node name targetNode

ref0 = NodeRef "Zero" 1
ref1 = NodeRef "One" 2
ref2 = NodeRef "Two" 0
refs = [ref0, ref1, ref2]

deref :: [NodeRef] -> Node
deref refs = head allNodes
    where
        deref' (NodeRef name targetIndex) = Node name (allNodes !! targetIndex)
        allNodes = map deref' refs

showme :: Node -> Int -> [String]
showme _ 0                      = []
showme (Node name target) count = name : showme target (count - 1)

main :: IO()
main = print $ showme (deref refs) 100
Run Code Online (Sandbox Code Playgroud)

不过我有几个问题:

创建了多少个 Node“实例”?

当我运行“showme”时,我是否在每一步中创建更多节点?

使用循环引用构建数据结构的正确方法是什么?

非常感谢您的回答。

Jon*_*rdy 5

\n

创建了多少个 Node“实例”?

\n
\n

通常,当您编写数据构造函数(例如NodeRef或 )时Node,这表示将分配这样的值\xe2\x80\x94cf 的计算。new Node(\xe2\x80\xa6)使用典型的命令式/OOP 语言。let当您使用或编写变量绑定时where,再次提及该名称将引用相同的值。

\n

顶级绑定或where不依赖于任何参数的绑定同样是共享的,也称为常量应用形式 xe2x80x9d (CAF) 。因此ref0ref1ref2、 和refs都是 CAF,并且任何提及 例如 都ref0将指代相同的NodeRef值。

\n

每次调用 时deref,它都会分配一个列表,其中包含的值与本例中输入列表 \xe2\x80\x94third 中的值allNodes一样多。该表达式引用同一共享列表的索引。NodeNodeRefrefsallNodes !! targetIndexallNodes

\n

最后,由于derefreturns head allNodes,只有那些实际连接到头节点的节点将保持可达,其他节点将被垃圾收集。

\n
\n

当我运行“showme”时,我是否在每一步中创建更多节点?

\n
\n

showme(:)分配与参数相等的列表 \xe2\x80\x9ccons\xe2\x80\x9d 单元格的数量count。或者,如果为count负数,它将继续递减,直到环绕范围Int并返回到0\xe2\x80\x94 您可以使用防护来<=代替,或者pred代替- 1使用检查的递减。它不复制节点本身,仅操作对它们(及其字段)的引用。

\n
\n

使用循环引用构建数据结构的正确方法是什么?

\n
\n

你的做法在这里是正确的。如果要使用这些循环数据结构,则需要注意在遍历它们时不要使用 nai\xcc\x88ve 无界递归,以避免不终止。您也不会\xe2\x80\x99能够轻松地更改此数据结构\xe2\x80\x94,即构造它的修改版本\xe2\x80\x94,而不会失去共享性。因此,\xe2\x80\x99 通常只使用 中基于 ID 的表示形式NodeRef,这是 \xe2\x80\x9cobservable 共享\xe2\x80\x9d 的示例,存储在父结构(例如IntMap. Node如果您想冻结 \xe2\x80\x9c 冻结\xe2\x80\x9d 表示,您可以移至 \ xe2\x80\x9c。

\n

打结更常用的是为了方便,以避免命令式语言中使用的显式突变和手动排序的需要。只要数据流是\xe2\x80\x99t循环的,那么你就可以编写整个数据结构的定义,并让普通的惰性求值过程填充它。

\n