Haskell 数据类型实例作为运算符
Posted
技术标签:
【中文标题】Haskell 数据类型实例作为运算符【英文标题】:Haskell Data Type Instance as operator 【发布时间】:2022-01-13 15:57:09 【问题描述】:我收到了一个练习来了解 haskell 中的数据类型,但我不知道如何解决这个问题。
他们给了我这样的数据:data CatLista a = Nil | Unit a| Conc (CatLista a) (CatLista a) deriving Eq
我需要使数据类型变为: Nil -> [] , Unit x -> [x] , Conc -> 与 (++) 相同的运算符
所以如果你运行 Conc (Unit 9)(Conc (Unit 5) (Unit 3)) == Conc (Conc (Unit 9) (Unit 5))(Conc (Unit 3) Nil)
应该给出 true 而 Conc (Unit 9)(Unit 3) == Conc (Unit 3) (Unit 9)
应该给出 false。
我已经尝试过像这样实例化 show 类:
instance Show a => Show (CatLista a) where
show a = case a of
Nil -> []
Unit x -> "[" ++ show x ++ "]"
Conc Nil dos -> show dos
Conc uno Nil -> show uno
Conc uno dos -> "[" + show uno ++ "," ++ show dos ++ "]"
我对haskell很陌生,所以我可能不知道一些基础知识,因为我不明白为什么如果我使用与下面Conc (Unit 9)(Conc (Unit 5) (Unit 3)) == Conc (Conc (Unit 9) (Unit 5))(Conc (Unit 3) Nil)
相同的命令运行它(beeing Conc uno dos -> show uno ++ show dos
)它仍然返回False,即使很难他们都返回与我的节目实例相同的 [9][5][3]。
编辑
感谢您,我做到了,它知道正确返回此代码预期的值:
toLista :: Eq a => CatLista a -> [a]
toLista Nil = []
toLista (Unit x) = [x]
toLista (Conc a b)
| a == Nil = toLista b
| b == Nil = toLista a
| otherwise = (++) (toLista a) (toLista b)
instance (Show a,(Eq a)) => Show (CatLista a) where
show a= show (toLista a)
但我仍然不知道为什么如果我尝试相同的比较它仍然返回 False,即使很难,我也会返回相同的 [9,5,3]。
这可能是由于我缺乏 Haskell 知识,对此感到抱歉。
【问题讨论】:
两个具有相同字符串表示的列表并不意味着这两个对象是等价的。您需要为CatLista
定义自己的Eq
实例。
首先定义toList :: CatLista a -> [a]
,然后Show
实例是toList
和show
的简单组合,用于列表。
您的定义假定Conc
值的字符串表示是两半的字符串表示的串联,而不仅仅是两半的元素。
【参考方案1】:
因为你在数据声明中包含了deriving Eq
,所以你告诉编译器生成一个默认的相等方法。编译器无法读懂您的想法,也无法知道您打算通过连接子列表来表示列表。
你知道你打算Conc [1, 2] [3]
代表与Conc [1] [2, 3]
相同的列表1;两者都应视为代表[1, 2, 3]
。编译器确实不知道这一点。生成的相等方法看到,在一种情况下,Conc
构造函数的第一个字段中有[1, 2]
,而在另一种情况下,我们有[1]
。这两个东西不相等,所以默认的相等方法说整个结构也不相等。
为避免这种情况,请停止使用 Show
实例。 Show
用于将 CatList
转换为将构建它的代码的 String
。它与测试相等性无关。您无法通过定义 Show
实例来更改 ==
的结果。
您需要从数据声明中删除deriving Eq
。这意味着编译器不会生成默认的相等方法,因此您可以编写自己的方法来做不同的事情(比较两个 CatList
s 是否代表同一个列表,而不是比较它们是否 具有相同的结构)。
1 我在这里滥用符号来使用括号语法编写子列表;这是不是有效的代码,我只是想让子结构的差异更容易看到。
【讨论】:
【参考方案2】:如果在对它们调用show
时产生相同的String
,则两个值不等价。 (==) :: Eq a => a -> a -> Bool
函数由Eq
typeclass 实现。因此,您需要自己为您的CatLista
实现Eq
的instance
。
如果你使用deriving Eq
,Eq
的默认实现是,如果两个值具有相同的数据构造函数,则它们是相同的,并且通过在这些参数上调用 (==)
,参数在元素上是等效的。
因此,您可以自己实现Eq
的实例:
data CatLista a = Nil | Unit a| Conc (CatLista a) (CatLista a) -- ← no deriving Eq
instance Eq a => Eq (CatLista a) where
ca == cb = toLista ca == toLista cb
【讨论】:
感谢您的回答。关于你回答的一个小问题可以帮助我:当你这样做时ca == cb = toLista ca == toLista cb
你的意思是,当它们的表示相等时,两个随机的 CatLista(ca 和 cb)是相等的(toLista ca == toLista cb
)?再次感谢
@AnderCarrera: toLista ca
将CataLista
转换为列表,因此如果它们与列表等价,则两者是等价的。因此,此函数not 表示如果两个CataLista
s 具有相同的show
,则它们是等价的,尽管因为你们都根据toLista
定义它,所以这里就是这种情况。跨度>
以上是关于Haskell 数据类型实例作为运算符的主要内容,如果未能解决你的问题,请参考以下文章