这是我第一次尝试创建类的自定义实例,例如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的新实例?
感谢
答案 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
位于Ord
且Num
,则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
或几个类似Sum
和Product
的幺半群)
现在您可以轻松地重复使用列表实例,也可以使用sum
。
答案 2 :(得分:2)
稍微概括一下@ Tikhon的方法,您也可以使用Monoid
代替Num
作为约束,在那里您已经与mconcat
预定义的“总和”(当然,您仍然需要Ord
)。这将为您提供更多类型而不仅仅是数字(例如List (List a)
,您现在可以轻松地递归定义)
另一方面,如果您 想要使用Num
作为幺半群,则必须每次都为Sum
或Product
做出决定。可以说,必须明确地写出这一点可以减少简洁性和可读性,但这是一种设计选择,取决于你最终希望拥有的普遍程度。
答案 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