我有一个复杂的递归数据结构,我将其简化为以下内容:
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
表达式a
和c
在概念上是相等的:它们都代表一个保存值1并指向表达式b的节点。我的问题是:如何在Haskell表达式中检查它们是否确实相等?如果我评估a == c
,它将永远评估循环结构中的子元素。
是否可以在Haskell中执行这样的比较?
编辑:在我的情况下,我试图比较两者用于检查/调试目的。但这样做的另一个原因可能是单元测试。答案 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的练习”。