为列表创建新的Ord实例

时间:2012-05-25 04:39:21

标签: haskell typeclass instances

这是我第一次尝试创建类的自定义实例,例如Ord。

我已经定义了一个新的数据结构来表示一个列表:

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

现在我想为List定义一个新的Ord实例,使List a&lt; = List b暗示“List a中元素的总和小于或等于List b中元素的总和”< / p>

首先,是否有必要定义一个新的“sum”函数,因为Prelude中定义的总和不适用于新的List数据类型?那么,我如何为列表定义Ord的新实例?

感谢

5 个答案:

答案 0 :(得分:12)

首先,这与普通列表实例完全不同。普通实例仅取决于可自行订购的列表项目;您的提案取决于他们的数字(例如在Num类中),因此更为狭窄。

有必要定义一个新的sum函数。令人高兴的是,将sum编写为简单的递归函数非常容易。 (巧合的是,你可以调用你的函数sum',它的发音为“sum prime”,按惯例意味着它的函数与sum非常相似。)

此外,该实例必须依赖于Num类以及Ord类。

一旦有了新的sum函数,就可以定义一个像这样的实例:

instance (Ord n, Num n) => Ord (List n) where compare = ... 
  -- The definition uses sum'

此实例声明可以理解为对于所有类型n,如果n位于OrdNum,则List n位于{{} 1}}比较的工作原理如下。语法非常类似于数学Ord的含义。希望这会使记忆更容易。

您必须对=>给出合理的定义。作为参考,compare的工作方式如下:如果compare a b返回a < b,如果LT则返回a = b,如果EQ则返回a > b 1}}。

这是一个很容易实现的功能,因此我将把它作为练习留给读者。 (我一直想说:P)。

答案 1 :(得分:4)

怎么样......

newtype List a = List [a]

如果您想为给定类型引入新的“不兼容”类型类实例,这是非常常见的(请参阅例如ZipList或几个类似SumProduct的幺半群)

现在您可以轻松地重复使用列表实例,也可以使用sum

答案 2 :(得分:2)

稍微概括一下@ Tikhon的方法,您也可以使用Monoid代替Num作为约束,在那里您已经与mconcat预定义的“总和”(当然,您仍然需要Ord)。这将为您提供更多类型而不仅仅是数字(例如List (List a),您现在可以轻松地递归定义)

另一方面,如果您 想要使用Num作为幺半群,则必须每次都为SumProduct做出决定。可以说,必须明确地写出这一点可以减少简洁性和可读性,但这是一种设计选择,取决于你最终希望拥有的普遍程度。

答案 3 :(得分:0)

怎么样..

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


instance (Ord a, Num a, Eq a) => Ord (List a) where

      -- 2 empty lists

      Empty <= Empty            =   True

      -- 1 empty list and 1 non-empty list

      Cons a b <= Empty         =   False
      Empty <= Cons a b         =   True

      -- 2 non-empty lists

      Cons a b <= Cons c d      =   sumList (Cons a b) <= sumList (Cons c d) 


-- sum all numbers in list

sumList         ::      (Num a) => List a -> a

sumList Empty               =       0
sumList (Cons n rest)       =       n + sumList rest

这是你在找什么?

答案 4 :(得分:0)

..或Prelude中具有sum函数的其他解决方案。

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


instance (Ord a, Num a, Eq a) => Ord (List a) where

      -- 2 empty lists

      Empty <= Empty            =   True

      -- 1 empty list and 1 non-empty list

      Cons a b <= Empty         =   False
      Empty <= Cons a b         =   True

      -- 2 non-empty lists

      Cons a b <= Cons c d      =   sum (listToList (Cons a b)) 
                                              <= sum (listToList (Cons c d)) 


-- convert new List to old one

listToList      ::      (Num a) => List a -> [a]

listToList Empty                =       []
listToList (Cons a rest)        =       [a] ++ listToList rest