7

我在haskell中有以下zip函数的实现

myzip (a:b) (z:g)
    | b == [] = []
    | g == [] = []
    | otherwise = (a,z) : myzip b g

当我将它加载到 ghci 时,出现以下错误

No instance for (Eq b)
  arising from a use of `=='
In the expression: g == []
In a stmt of a pattern guard for
               an equation for `myzip':
  g == []
In an equation for `myzip':
    myzip (a : b) (z : g)
      | b == [] = []
      | g == [] = []
      | otherwise = (a, z) : myzip b g

失败,加载模块:无。

我真的不确定为什么这不起作用。任何人都可以给我任何帮助吗?

4

1 回答 1

14

实际上你在问题中给出的函数编译得很好。如果你所拥有的是,你得到你引用的错误:

myzip :: [a] -> [b] -> [(a, b)]
myzip (a:b) (z:g)
    | b == [] = []
    | g == [] = []
    | otherwise = (a, z) : myzip b g

使用显式类型签名表示myzip适用于任何类型的列表ab. 但是你用过b == []and g == []。相等运算符在任何类型上定义,仅在作为类型类成员的类型上定义Eq,因此您编写的代码与您提供的类型不匹配。

这就是错误消息直截了当地说的内容,但是如果您只是在学习并且还没有开始输入类,那么它有点不清楚。

如果您更改类型签名myzip来说明a并且b需要成为Eq类型类的成员,那么您提供的代码将起作用:

myzip :: (Eq a, Eq b) => [a] -> [b] -> [(a, b)]

或者,如果您完全关闭类型签名(就像您在问题中所做的那样),GHC 实际上会根据您使用运算符的事实推断出这种类型==,并且代码只是按原样编译。

但是,可以不使用==运算符来检查列表是否为空,因此您可以编写myzip使其确实对任何类型ab. 一种方法是使用该null功能:

myzip :: [a] -> [b] -> [(a, b)]
myzip (a:b) (z:g)
    | null b = []
    | null g = []
    | otherwise = (a, z) : myzip b g

但更常见的方法是简单地使用多个方程来定义myzip,基本情况与模式匹配,[]主要情况假设列表非空:

myzip :: [a] -> [b] -> [(a, b)]
myzip (a:[]) _ = []
myzip _ (z:[]) = []
myzip (a:b) (z:g) = (a, z) : myzip b g

请注意,这种风格也很明显地表明您的实现中存在错误。您正在丢弃最后一个aor z,并且列表完全为空时没有任何情况!

当你的方程式说myzip (a:b) (z:g)然后检查b对照空列表时,g它实际上检查错误的东西太晚了。您不需要检查 if bis [],您需要检查整个列表是否为空。但是您已经假设它不是空的并将其分解为a:b. 这会导致您的代码 (a) 返回错误的结果,因为它丢弃了应该压缩的最后一对元素,并且 (b) 当其中一个参数是空列表时产生错误。

列表上的递归通常看起来更像这样:

myzip :: [a] -> [b] -> [(a, b)]
myzip [] _ = []
myzip _ [] = []
myzip (a:b) (z:g) = (a, z) : myzip b g

这行为正确。

于 2013-05-05T04:45:05.587 回答