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 实例是toListshow 的简单组合,用于列表。 您的定义假定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。这意味着编译器不会生成默认的相等方法,因此您可以编写自己的方法来做不同的事情(比较两个 CatLists 是否代表同一个列表,而不是比较它们是否 具有相同的结构)。


1 我在这里滥用符号来使用括号语法编写子列表;这是不是有效的代码,我只是想让子结构的差异更容易看到。

【讨论】:

【参考方案2】:

如果在对它们调用show 时产生相同的String,则两个值不等价。 (==) :: Eq a => a -> a -> Bool 函数由Eq typeclass 实现。因此,您需要自己为您的CatLista 实现Eqinstance

如果你使用deriving EqEq 的默认实现是,如果两个值具有相同的数据构造函数,则它们是相同的,并且通过在这些参数上调用 (==),参数在元素上是等效的。

因此,您可以自己实现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 caCataLista 转换为列表,因此如果它们与列表等价,则两者是等价的。因此,此函数not 表示如果两个CataListas 具有相同的show,则它们是等价的,尽管因为你们都根据toLista 定义它,所以这里就是这种情况。跨度>

以上是关于Haskell 数据类型实例作为运算符的主要内容,如果未能解决你的问题,请参考以下文章

为自己的数据类型定义 Ord 实例

Haskell类具有多态数据定义的实例

Haskell中不同类型之间的关系

Haskell 算术运算和任意/固定精度数的 DB 持久性

Haskell 多态性和类型类实例

使用 haskell 读取和类型类 - 模棱两可的类型变量错误