Data.Set和自定义Ord实例的结果不一致

时间:2018-12-31 04:33:24

标签: haskell typeclass

这是我的数据结构

data Ex =
  P String
  | (:←) Ex

它具有p == ←←p的属性。我的自定义Eq和Ord实例尝试定义它们。但是,我看到test3(从[p,←←p,←p]创建的集合)和test4(从[p,←p,←←p]创建的集合)的结果不一致。结果如下图所示:

*Test> test3
fromList [←q,←←q]
*Test> test4
fromList [q,←q,←←q]

请注意,test3和test4仅在创建集合的元素顺序上有所不同。然而,结果有所不同。

我认为使用Data.Set.fromList创建集合的顺序并不重要。有人可以帮助我找到我的Eq或Ord实例错误吗?以下完整代码,使用GHC 8.4.3编译。

module Test where    
import Data.Set as S


data Ex =
  P String
  | (:←) Ex

instance Show Ex where
  show (P s) = s
  show ((:←) e) = "←" ++ (show e)

instance Eq Ex where
  (P s1) == (P s2) = s1 == s2
  (:←) e1 == (:←) e2
    | e1 == e2 = True
    | otherwise = False
  e1 == (:←) e2
    | e1 == e2 = False
    | (:←) e1 == e2 = True
    | otherwise = False
  (:←) e1 == e2
    | e1 == e2 = False
    | e1 == (:←) e2 = True
    | otherwise = False

elength :: Ex   -> Int
elength (P s) = length s
elength ((:←) e) = elength e + 1

instance Ord Ex where
  compare e1 e2
    | e1 == e2 = EQ
    | otherwise = if (elength e1) <= (elength e2) then LT
      else GT

-- Check that ←q == ←←q                                                                                               
test2 = S.fromList [(:←) ((:←) (P "q")), P "q"]
-- output should be : {←←q, ←q}                                                                                       
test3 = S.fromList [P "q",  (:←) ((:←) (P "q")), (:←) (P "q")]  
-- output should be same as that of test3 : {←←q, ←q}                                                                 
test4 = S.fromList [P "q", (:←) (P "q"), (:←) ((:←) (P "q"))]

编辑:

  1. 请注意,如果我修改elength定义来处理这种情况,则不一致之处消失了。

    elength ((:←) ((:←) e)) = elength e

  2. elength==的情况下,也许我的q度量和←←q的定义不一致。我仍然想知道他们到底在哪里出错

1 个答案:

答案 0 :(得分:6)

您的情商实例在我看来确实很奇怪。我会一次取消两个被取消的配对,而不是零散的:

instance Eq Ex where
  (P s1) == (P s2)     = s1 == s2
  ((:←) e1) == (:←) e2 = e1 == e2
  e1 == (:←) ((:←) e2) = e1 == e2
  (:←) ((:←) e1) == e2 = e1 == e2
  _ == _ = False

也许这等于您所写的内容;很难说出来,因为您的模式匹配与您的目标不太吻合。

您的Ord实例也是一个问题,因为您没有定义一致的顺序。对于x y z的任何项目集,其中x < y && y < z应该是x < z。但是,根据您的规则,有一些简单的反例:

x = P "a"
y = (P "b" :←)
z = ((P "a" :←) :←)

尽管x == zy之间,但在这里。{p1

解决这两个问题的一种方法是编写一个simplify函数,该函数删除所有成对的取消构造函数,并在EqOrd实例中使用该函数。简化每个参数,以便您知道它们各自具有0或1个否定级别。从这里开始,Eq很简单,定义Ord之前您需要做的就是确定一个负值是否小于或大于一个非负值。您无法选择它们相等,因为这再次破坏了传递性。

如果您确实写了simplify,那么每次触摸这些对象之一时,调用简化会浪费很多工作,因为您会立即放弃简化。我选择不为该类型导出构造函数,而是提供一个在返回值之前进行简化的智能构造函数。然后,消费者将知道一切都被否定一次或根本没有否定。