Haskell中用于比较列表的Eq存在问题

Haskell中用于比较列表的Eq存在问题,haskell,methods,equality,algebraic-data-types,custom-data-type,Haskell,Methods,Equality,Algebraic Data Types,Custom Data Type,我自己在Haskell中实现列表,但在比较两个列表是否相等时遇到了一个小问题 data List a = Void | Cons a (List a) -- deriving (Show, Eq) instance (Eq a) => Eq (List a) where (==) a b = equalHelp a b equalHelp :: (Eq a) => List a -> List a -> Bool equalHelp Void Void = Tru

我自己在Haskell中实现列表,但在比较两个列表是否相等时遇到了一个小问题

data List a = Void | Cons a (List a) -- deriving (Show, Eq)

instance (Eq a) => Eq (List a) where
  (==) a b = equalHelp a b

equalHelp :: (Eq a) => List a -> List a -> Bool
equalHelp Void Void = True
equalHelp a Void = False
equalHelp Void b = False
equalHelp (Cons a b) l = if (myElem a l) then (equalHelp b l) else False

myElem :: (Eq a) => a -> List a -> Bool
myElem a Void = False
myElem a (Cons c d) = if (a == c) then True
                      else myElem a d
例如,如果我有

l1 = (Cons 1 (Cons 2 (Cons 3 (Cons 4 (Cons 5 Void)))))
如果我做了
l1==l1
,那么它就不会打印
True
,而是打印
False

我遗漏了什么?

您的
equalHelp
实现了集的
isSubset子集
谓词——几乎实现了,除了它总是滑入空的第二个列表大小写,然后失败

相反,请停止单例情况下的递归:

isSubset :: (Eq a) => List a -> List a -> Bool
isSubset Void Void = True
isSubset a Void = False
isSubset Void b = False
isSubset (Cons a Void) l = if (myElem a l) then True           else False
isSubset (Cons a b)    l = if (myElem a l) then (isSubset b l) else False
由于前面的子句,您的第二个子句假定
a
是一个非空列表。但如果每个子句的假设都是公开的,并且模式是互斥的(如果这样做不会使我们的代码过于冗长,因而可读性降低的话),则更好。因此,我们把它写成

isSubset :: (Eq a) => List a -> List a -> Bool
isSubset Void         Void  =  True
isSubset (Cons _ _)   Void  =  False
isSubset Void  (Cons _ _)   =  False
isSubset (Cons a Void)  l   =  myElem a l
isSubset (Cons a b)     l   =  myElem a l && isSubset b l
这里故意留下了一个错误,代码的痕迹。其中一个子句是错误的:空集是的子集。因此,作为第一个参数处理空集的两个子句可以合并为一个子句

我们还使用了代码简化

if A then True else False   ===   A
if A then B    else False   ===   A && B
在这篇文章中,我重新写了一遍。还有

if A then True else C       ===   A || C
您可以使用它简化您的
myElem
定义


另一种纠正代码的方法是,将其视为定义列表的相等性直至元素的顺序。按照注释中的建议,在每次递归调用中删除第二个参数的head元素也不会起作用,因为我们需要删除找到的元素,该元素可能不是head元素,也可能不是head元素。这可能不是很简单,也不是很有效,特别是如果您还选择允许元素的多样性的话。在这种情况下,只需定义

equalUpToOrderingWithMults a b =
    isSubset a b  &&  isSubset b a

最直接的方法是放弃MyLem调用,而是在递归的每一步仅比较两个参数列表的头元素,以获得“正常”的相等概念。

(1)如果取两个相等的列表,并从其中一个列表中删除第一个元素,则它们将不再相等。(2) 在任何情况下,您都不需要
myElem
来实现
(==)
;只需在每一步直接比较列表标题。
如果A then B else False
A&&B
相同。
如果A then True else B
A | | B
@molbdnilo yes相同。到目前为止,我还没有读过这本书。:)