【问题标题】:How do I compare recursive data structures?如何比较递归数据结构?
【发布时间】:2013-01-22 16:55:40
【问题描述】:

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

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 中进行这样的比较?

编辑:就我而言,我试图将两者进行比较以进行检查/调试。但这样做的另一个原因可能是单元测试。

【问题讨论】:

  • 你为什么要这样做?
  • 看到我做这个项目是为了学习 Haskell 并测试它的所有可能性,我想尽可能避免使用 GHC 特定的扩展。
  • 除非你给每个节点一个明确的 ID 值并使用 ID 比较它们,否则没有 GHC 扩展是不可能的(即使有它们也是不确定的)。
  • 一般情况下是不可能的。问题是语义相等是不可判定的。

标签: haskell


【解决方案1】:

首先ab不相等,但是ac相等,不只是概念上的,但实际上它们是一回事。

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

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

这个想法是你有一个单独的Map,从 k 类型的键到节点。但是,您不能为该实例编写 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 的概念。

【讨论】:

  • 对不起,我的意思是说ac 相等,而不是ab。我已经纠正了我的问题。感谢您的明确回答,我将测试这些解决方案是否适用于我的情况并尽快回复您!
  • 可观察共享概念非常有趣,但似乎与 Haskell 的哲学背道而驰。我将其标记为答案的原因是第一句话:“ac 是相等的,不仅在概念上,而且它们实际上是同一件事”。这意味着如果我想区分两个拥有相同值并指向同一个“下一个”节点的节点,那么我需要修改节点的定义以包含某种唯一 ID 字段。非常感谢!
【解决方案2】:

如果您承诺仅将其用于测试和调试,而不用于应用程序代码,您还可以使用我的 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 的练习”。

【讨论】:

    猜你喜欢
    • 2021-06-27
    • 1970-01-01
    • 1970-01-01
    • 2014-12-08
    • 2015-04-26
    • 2018-08-27
    • 1970-01-01
    • 2017-08-06
    • 1970-01-01
    相关资源
    最近更新 更多