1

我有这个haskell代码。我已经创建了两种数据类型,然后我想创建一个Mord可以将函数与Mlist类型进行比较的新类。

import Data.List

data Mlist a = Mlist [a]

data Mordering = MLT deriving (Eq, Show)

s = Mlist [1, 2, 3]
t = Mlist [1, 4, 2, 3]

class Mord a where 
    mcompare :: a -> a -> Mordering


instance Mord a => Mord (Mlist a) where
    mcompare (Mlist xs) (Mlist ys) = MLT

但如果我尝试mcompare s t我得到

<interactive>:1:1:
    No instance for (Mord Integer)
      arising from a use of `mcompare'
    Possible fix: add an instance declaration for (Mord Integer)
    In the expression: mcompare s t
    In an equation for `it': it = mcompare s t

有没有人看到问题?

编辑

这是我的新代码:

import Data.List

data Mlist a = Mlist [a]

data Mordering = MEQ | MIN deriving (Eq, Show)

s = Mlist [1, 2, 3]
t = Mlist [1, 4, 2, 3]

class Mord a where 
    mcompare :: a -> a -> Mordering

instance Mord (Mlist a) where
    mcompare (Mlist xs) (Mlist ys)
           | length xs == length ys && null (xs \\ ys) = MEQ
           | otherwise = MIN

但我现在得到的错误是:

    No instance for (Eq a)
      arising from a use of `\\'
    In the first argument of `null', namely `(xs \\ ys)'
    In the second argument of `(&&)', namely `null (xs \\ ys)'
    In the expression: length xs == length ys && null (xs \\ ys)
Failed, modules loaded: none.
4

3 回答 3

4

哈斯克尔读到:

instance Mord a => Mord (Mlist a) where
    mcompare (Mlist xs) (Mlist ys) = MLT

意思是“对于每个a我有 的实例Mord,我也有 的Mord实例Mlist a。但是,如果我没有 的Mord实例a,那么我也没有 的Mord实例Mlist a。对不起,但让我们再出去玩有时间!”

实例声明的Mord a部分称为上下文;您可以在A Gentle Introduction to Haskell中阅读更多相关信息。

[]实例上下文对类型构造函数很有用,例如Mlist您的代码。例如,我们可以查看标准Eq和它[a]实例

instance (Eq a) => Eq [a] where
    []     == []     = True
    (x:xs) == (y:ys) = x == y && xs == ys
    _xs    == _ys    = False

这个实例是什么意思?两个空列表相等;如果两个非空列表的头部相等且尾部相等,则它们相等;任何其他两个非空列表不相等;空列表和非空列表不相等。

我将上一段的部分加粗,因为它有助于回答“为什么实例上下文有用?”这个问题。嗯,在列表示例中,列表相等的定义一部分来自列表的结构,但另一部分是基于其元素的相等性。换句话说,除非您可以比较其元素是否相等,否则您无法比较列表是否相等。

函数是无法有意义地比较相等的值的经典示例。那么我们如何检查函数列表是否相等呢?

ghci> (+1) == (*2)
<interactive>:6:6:
    No instance for (Eq (a0 -> a0))
      arising from a use of `=='
    Possible fix: add an instance declaration for (Eq (a0 -> a0))
    In the expression: (+ 1) == (* 2)
    In an equation for `it': it = (+ 1) == (* 2)

ghci> [(+4)] == [\x -> 2 * x / 3]
<interactive>:8:8:
    No instance for (Eq (a0 -> a0))
      arising from a use of `=='
    Possible fix: add an instance declaration for (Eq (a0 -> a0))
    In the expression: [(+ 4)] == [\ x -> 2 * x / 3]
    In an equation for `it': it = [(+ 4)] == [\ x -> 2 * x / 3]

因此,为了让您的代码正常工作,您可以删除未使用的实例上下文,或者您需要提供适当的底层实例,即:

instance Mord Int where
    mcompare ... = ...

s :: Mlist Int
s = Mlist [1, 2, 3]
t :: Mlist Int
t = Mlist [1, 4, 2, 3]
于 2013-03-13T17:28:22.357 回答
3
instance Mord a => Mord (Mlist a) where

在这里你说那Mlist aMord当且仅当a的一个实例MordInteger不是 的实例Mord,因此Mlist Integer也不是 的实例Mord。如果你定义一个实例Mord Integer,你的代码就可以工作。

作为对您的编辑的回应:您的新代码不起作用,因为\\仅适用于可以比较相等性的值列表(即作为类型类实例的类型的值Eq)。由于您对实例没有Eq约束,因此您说它应该适用于所有类型,但由于\\不适用于所有类型,因此您不能这样做。Eq向您的实例(即)添加约束instance (Eq a) => Mord (Mlist a) ...将解决您的问题。

于 2013-03-13T15:38:35.180 回答
0

我将放大一个特定点,其他答案已经涵盖了当前的问题。

假设您在目前获得的帮助下设法让一切正常工作,并且您已经启动了 GHCi 并加载了您的代码。让我们试一试:

ghci> mcompare s t
MLT
ghci> mcompare t s
MLT

唔。这对我来说似乎不是很有用!这是怎么回事?

如果我们看看在mcompare给定两个Mlists 时实际做了什么——准确地说,在这个实例声明中:

instance Mord a => Mord (Mlist a) where
    mcompare (Mlist xs) (Mlist ys) = MLT

我们可以看到根本没有比较;不管是什么xsysmcompare只是返回MLT。在某种程度上这是合乎逻辑的,因为Mordering数据类型只有一个值:

data Mordering = MLT deriving (Eq, Show)

但是要表示比较操作的结果,您通常需要 3 个可能的结果:第一个参数小于、等于或大于第二个参数。您可能会争辩说还有一个“无与伦比”的选择,但我们不要在那里讨论!

因此,您希望您的Mordering数据类型包含这三个比较结果,您可以通过添加更多构造函数来做到这一点(这就是我在 sepp2k 答案的评论中所说的),如下所示:

data Mordering = LessThan 
               | EqualTo 
               | GreaterThan 
                   deriving (Eq,Show)

(我猜你的意思是data Mordering = MLT | MEQ | MGT deriving (Eq, Show)也许?)

这会带来一系列新的问题,我会给你一些提示来帮助你。


首先,我们想要mcompare 3 4成为LessThanmcompare 5 5成为EqualTomcompare 7 6成为GreaterThan,因为 3 小于 4(等等)。为此,您需要一个Mordfor的实例Integer

instance Mord Integer where
    mcompare x y
        | x <  y = ...
        | x == y = ...
        | x >  y = ...

填空应该不会太难,我希望!

第二部分有点棘手:既然我们可以比较 type 的值,a我们如何比较 type 的值Mlist a?换句话说,声明应该instance Mord a => Mord (Mlist a)怎么写?will的结果mcompare (Mlist xs) (Mlist ys)取决于是否xs为空,是否ys为空,如果两者都不为空,取决于第一个元素如何比较xsys其余元素如何比较。

我会把它留给你弄清楚;不要犹豫,提出一个新问题,表明你走了多远,你被困在哪里!

于 2013-03-13T21:00:42.470 回答