我正在尝试为图表构建数据结构。
以下代码似乎按预期工作:
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”时,我是否在每一步中创建更多节点?
使用循环引用构建数据结构的正确方法是什么?
非常感谢您的回答。
\n\n创建了多少个 Node“实例”?
\n
通常,当您编写数据构造函数(例如NodeRef
或 )时Node
,这表示将分配这样的值\xe2\x80\x94cf 的计算。new Node(\xe2\x80\xa6)
使用典型的命令式/OOP 语言。let
当您使用或编写变量绑定时where
,再次提及该名称将引用相同的值。
顶级绑定或where
不依赖于任何参数的绑定同样是共享的,也称为常量应用形式 xe2x80x9d (CAF) 。因此ref0
,ref1
、ref2
、 和refs
都是 CAF,并且任何提及 例如 都ref0
将指代相同的NodeRef
值。
每次调用 时deref
,它都会分配一个列表,其中包含的值与本例中输入列表 \xe2\x80\x94third 中的值allNodes
一样多。该表达式引用同一共享列表的索引。Node
NodeRef
refs
allNodes !! targetIndex
allNodes
最后,由于deref
returns head allNodes
,只有那些实际连接到头节点的节点将保持可达,其他节点将被垃圾收集。
\n\n当我运行“showme”时,我是否在每一步中创建更多节点?
\n
showme
(:)
分配与参数相等的列表 \xe2\x80\x9ccons\xe2\x80\x9d 单元格的数量count
。或者,如果为count
负数,它将继续递减,直到环绕范围Int
并返回到0
\xe2\x80\x94 您可以使用防护来<=
代替,或者pred
代替- 1
使用检查的递减。它不复制节点本身,仅操作对它们(及其字段)的引用。
\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。
打结更常用的是为了方便,以避免命令式语言中使用的显式突变和手动排序的需要。只要数据流是\xe2\x80\x99t循环的,那么你就可以编写整个数据结构的定义,并让普通的惰性求值过程填充它。
\n