-2

我正在尝试为替代版本编号方法定义 Eq 运算符。

type VersionCompound = Maybe Int -- x, 0, 1, 2, ...

type VersionNumber = [VersionCompound] -- x.x, x.0, x.1, x.2, ... , 1.0, 1.1, 1.2, ... 1.x.x, 2.x.x, 3.x.x, ...

instance Eq VersionNumber where
        [] == [] = True
        (x:[]) == (y:[]) = x == y
        (Nothing:xs) == ys = (xs == ys)
        xs == (Nothing:ys) = (xs == ys)

预计它会True在以下情况下返回:x.x.x == x1.x.x == x.1.x.xx.1 == 1等。但它会返回错误:

VersionNumber.hs:58:34:
    Overlapping instances for Eq [VersionCompound]
      arising from a use of ‘==’
    Matching instances:
      instance Eq a => Eq [a] -- Defined in ‘GHC.Classes’
      instance Eq VersionNumber -- Defined at VersionNumber.hs:55:10
    In the expression: (xs == ys)
    In an equation for ‘==’: (Nothing : xs) == ys = (xs == ys)
    In the instance declaration for ‘Eq VersionNumber’

任何想法如何解决它?

编辑:我通过列表上的模式匹配解决这个问题的方法被证明是不完整的。我想忽略给定版本左侧的任意x's(或s)列表。Nothing因此,例如,x.x.x.x.x将等于x.x.x和 to x。同样,x.x.x.1将等于x.x.11。如果x中间有一个,它就不会被扔掉。因此,对于这种情况,x.x.1.x.0将等于x.1.x.01.x.0。又一个例子:x.1.x.x.0.x等于1.x.x.0.xx.1.x.0.x等于1.x.0.x(您只需删除x左侧的 's)。

修复错误后我一直在努力解决的Overlapping instances for Eq [VersionCompound]是如何获得x.x.x == x->True模式匹配。但是,正如@WillemVanOnsem 出色地指出的那样,它不应该通过模式匹配来实现,而应该通过函数组合来实现。

PS。我个人鼓励您投票赞成@WillemVanOnsem 的答案,因为他的解决方案非常优雅,需要一些努力才能提出并代表了 Haskell 力量的本质。

4

1 回答 1

6

您使用类型别名。这意味着您没有定义单独的类型VersionNumberVersionCompound; 您只是构建了一个别名。在窗帘后面,Haskell 看得VersionNumber一清二楚[Maybe Int]

现在,如果我们查看 Haskell 的库,我们会看到:

instance Eq Int where
    -- ...

instance Eq a => Eq (Maybe a) where
    -- ...

instance Eq a => Eq [a] where
    -- ...

所以这意味着它Eq Int被定义,也Eq (Maybe Int)被定义,因此它Eq [Maybe Int]也被 Haskell 库定义。所以你实际上已经构建了一个Eq VersionNumber没有写的。现在您尝试编写一个额外的,当然,编译器会混淆选择哪个。

有一些方法可以解决重叠的实例,但这可能只会产生更多的麻烦。

因此,您最好data用一个构造函数构造一个类型。例如:

type VersionCompound = Maybe Int
data VersionNumber = VersionNumber [VersionCompound]

现在,由于只有一个构造函数,您最好使用 a newtype

type VersionCompound = Maybe Int
newtype VersionNumber = VersionNumber [VersionCompound]

现在我们可以定义我们的特殊实例,例如:

instance Eq VersionNumber where
    (VersionNumber a) == (VersionNumber b) = a =*= b
        where [] =*= [] = True
              (x:[]) =*= (y:[]) = x == y
              (Nothing:xs) =*= ys = (xs =*= ys)
              xs =*= (Nothing:ys) = (xs =*= ys)

因此,我们解开构造函数,然后使用另一个本地定义的函数=*=,其工作方式与您在问题中定义的一样。

但是请注意,您在程序中忘记了一些模式,例如Just x : xs左侧和右侧。所以,你最好先解决这些问题。如果我通过编译器运行您的代码,我会收到以下警告:

Pattern match(es) are non-exhaustive
In an equation for ‘=*=’:
    Patterns not matched:
        [] (Just _:_)
        [Just _] []
        [Just _] (Just _:_:_)
        (Just _:_:_) []

你想如何处理这些情况当然取决于你。@DanielWagner 建议使用:

import Data.Function(on)
import Data.Maybe(catMaybes)

instance Eq VersionNumber where
    (VersionNumber a) == (VersionNumber b) = on (==) catMaybes a b

这将过滤掉Nothing两个VersionNumbers 的值,然后检查是否Just生成相同的列表中的值。所以3.x.2.x.x.1将等于3.2.1x.x.x.x.x.3.2.1

编辑:根据您在评论中的规范,您可能正在寻找:

import Data.Function(on)

instance Eq VersionNumber where
    (VersionNumber a) == (VersionNumber b) = on (==) (dropWhile (Nothing ==)) a b
于 2017-08-07T20:33:19.063 回答