【问题标题】:"True" pure functional doubly-linked list and Sharing of nodes“真”纯函数式双向链表和节点共享
【发布时间】:2012-02-16 10:32:18
【问题描述】:

Recently 我被介绍给this OCaml code,在 Haskell 中可以写成:

data DL a = DL [a] a [a]

create [] = error "empty list"
create (x:xs) = DL [] x xs

next (DL pr x (h:tl)) = DL (x:pr) h tl
next _ = error "end of dlist"

prev (DL (p:pr) x tl) = DL pr p (x:tl)
prev _ = error "start of dlist"

我认为 不是 一个合适的双向链表实现,因为它在遍历时创建了新的存储。 OTOH 还有this Haskell code:

data DList a = Leaf | Node { prev::(DList a), elt::a, next::(DList a) }

create = go Leaf
  where go _    []     = Leaf
        go prev (x:xs) = current
            where current = Node prev x next
                  next    = go current xs

我们可以说只有这个代码是真正的 dl-list吗?

我们能否依靠这段代码引入真正的 dl-list 节点共享,从而在遍历时不会创建新的存储?

Haskell 中的同名变量是否总是指代相同的“事物”,还是可能单独出现的同名变量引用同一事物的不同副本? strong>(经过编辑以增加重点)。

【问题讨论】:

  • 第一个实现是所谓的Zipper;可以说它可以用于单链表或双链表。但是,它本身并不是一个列表实现。
  • 如果您仔细阅读 Haskell 报告,您将找不到任何关于如何表示数据的段落。请记住,所有类型的共享都依赖于实现,尽管实现某个功能的方式大多只有几种合理的方式。
  • 感谢您提出这个问题!我对我的不满意,正在关注这个。
  • @WillNess GHC 非常注意不要重复计算。一旦一个表达式被计算为 WHNF,它就可以被复制而无需重复工作。
  • 我不是 100% 确定 GHC 对空间安全,但在正常情况下它不会复制数据。

标签: haskell functional-programming linked-list


【解决方案1】:

您可以使用名为 Vacuum-cairo 的包来可视化数据结构的内存布局。使用cabal install vacuum-cairo 从hackage 安装,然后您应该能够在GHCi 中通过类似这样的方式验证这两种结构的差异:

> import System.Vacuum.Cairo
> view $ create [1..5]

在那里你可以看到节点是使用 DList 共享的,因为 DL 是两个列表,中间有一个元素(正如所指出的,这是一种 Zipper)。

注意:这是特定于 GHC 的,不同的实现可能会以不同的方式表示内存中的数据,但这很典型。

【讨论】:

  • 谢谢,我了解它应该是什么。但是有人建议(在我的问题中第一个单词链接到的帖子中)不能保证共享。那么,结构应该如何,cairo 将如何向我展示,不一定是现实中的样子。我想知道,在任何实现中,是否至少可以保证同名 var 始终指向内存中的同一事物?
  • 是的,否则这样的示例将不起作用:(x, xs) = let y = replicate 10 x in (length y, y)
  • 我认为它仍然可以工作,因为在您的示例中,依赖流中没有循环。
  • @WillNess 正如 FUZxxl 所述,Haskell 报告未指定共享行为。 GHC 通常按您的预期工作,而当它不正常时,它通常与您的预期有很小的偏差。鉴于 Haskell 的指定方式,作为程序员,您应该无法区分共享实现(价值方面),除非执行计算所需的时间。
  • @DanBurton 谢谢!时间空间……但这让我有点不安。我不喜欢“通常”或“通常”这个词,我喜欢“保证”。它使我处于控制之中,而不是受其摆布。 :) 如果 Haskell The Language 不遗余力地承诺任何事情,并假装我们编写的是等式规范而不是程序,那么它确实让我们摆脱了等式高级规范,并超出我们的期望,并自行执行所有转换,从而获得最快和最精简的代码。 :) :) 那么我很乐意接受缺乏规范。 :)
【解决方案2】:

我建议后者是“正确”的实现,是的。

我没有事实可以支持这一点,但在我看来,鉴于我对 GHC 实施的理解,后者应该按照您期望的双链表的工作方式工作。

【讨论】:

  • 谢谢!我想确定一下,尤其是关于我的最后一个问题。 :)
  • @WillNess 我认为这是一个实现问题,但 GHC 确实在底层使用基于指针的表示来表示相同的事物(例如,在 xs++ys 中,原始 ys 值用作新组合列表的末尾)。
猜你喜欢
  • 2010-12-23
  • 1970-01-01
  • 1970-01-01
  • 2011-07-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多