如何比较递归数据结构?

时间:2013-01-21 23:19:34

标签: haskell

我有一个复杂的递归数据结构,我将其简化为以下内容:

data Node = Node { value :: Integer, next :: Node } deriving (Show,Eq)

给出以下表达式:

--Create a circular structure
a = Node 1 b
b = Node 0 a --Tie the knot
c = Node 1 b --Another structure which points to b

表达式ac在概念上是相等的:它们都代表一个保存值1并指向表达式b的节点。我的问题是:如何在Haskell表达式中检查它们是否确实相等?如果我评估a == c,它将永远评估循环结构中的子元素。

是否可以在Haskell中执行这样的比较?

编辑:在我的情况下,我试图比较两者用于检查/调试目的。但这样做的另一个原因可能是单元测试。

2 个答案:

答案 0 :(得分:12)

首先, a b 不相等,但 a c 是相等的,不是只是在概念上,但它们实际上是相同的。

回答您的问题:您的问题没有直接解决方案。如果您需要进行身份比较,首先必须建立身份概念。一种方法是从节点的键到Map

data Node k =
    Node {
      nodeValue :: Integer,
      nodeNext  :: k
    }

我们的想法是,从 k 类型的键到节点的单独Map。但是,您无法为该实例编写Eq实例。一种稍微优雅的解决方法是使用reflection

{-# LANGUAGE ScopedTypeVariables #-}

import Data.Reflection

data Node n k =
    Node {
      nodeValue :: Integer,
      nodeNext  :: k
    }

instance (n `Reifies` Map k (Node n k)) => Eq (Node n k) where
    (==) = {- ... -}
        where
        nodeMap :: Map k (Node n k)
        nodeMap = reflect (Proxy :: Proxy n)

最近获得一些关注的另一个选择是observable sharing的概念。

答案 1 :(得分:-1)

如果您承诺仅将其用于测试和调试,而不是用于应用程序代码,您还可以使用我的ghc-heap-view库,它为您提供了对Haskell堆的低级视图,并使您能够实施比较:

Prelude> :script /home/jojo/.cabal/share/ghc-heap-view-0.4.0.0/ghci 
Prelude> data Node = Node { value :: Integer, next :: Node } deriving (Show,Eq)
Prelude> let { a = Node 1 b; b = Node 0 a ; c = Node 1 b}
Prelude> take 100 (show a) -- make sure it is evaluated, we are not interested in thunks here
"Node {value = 1, next = Node {value = 0, next = Node {value = 1, next = Node {value = 0, next = Node"
Prelude> take 100 (show c) -- dito
"Node {value = 1, next = Node {value = 0, next = Node {value = 1, next = Node {value = 0, next = Node"
Prelude> System.Mem.performGC
Prelude> :printHeap a
let x0 = Node (S# 1) (Node (S# 0) x0)
in x0
Prelude> :printHeap c
let x2 = Node (S# 0) (Node (S# 1) x2)
in Node (S# 1) x2

但是现在这对你来说当然不是正确的事情,因为你“正在做这个项目作为学习Haskell的练习”。