实际上你在问题中给出的函数编译得很好。如果你所拥有的是,你会得到你引用的错误:
myzip :: [a] -> [b] -> [(a, b)]
myzip (a:b) (z:g)
| b == [] = []
| g == [] = []
| otherwise = (a, z) : myzip b g
使用显式类型签名表示myzip
适用于任何类型的列表a
和b
. 但是你用过b == []
and g == []
。相等运算符未在任何类型上定义,仅在作为类型类成员的类型上定义Eq
,因此您编写的代码与您提供的类型不匹配。
这就是错误消息直截了当地说的内容,但是如果您只是在学习并且还没有开始输入类,那么它有点不清楚。
如果您更改类型签名myzip
来说明a
并且b
需要成为Eq
类型类的成员,那么您提供的代码将起作用:
myzip :: (Eq a, Eq b) => [a] -> [b] -> [(a, b)]
或者,如果您完全关闭类型签名(就像您在问题中所做的那样),GHC 实际上会根据您使用运算符的事实推断出这种类型==
,并且代码只是按原样编译。
但是,可以不使用==
运算符来检查列表是否为空,因此您可以编写myzip
使其确实对任何类型a
和b
. 一种方法是使用该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
请注意,这种风格也很明显地表明您的实现中存在错误。您正在丢弃最后一个a
or z
,并且列表完全为空时没有任何情况!
当你的方程式说myzip (a:b) (z:g)
然后检查并b
对照空列表时,g
它实际上检查错误的东西太晚了。您不需要检查 if b
is []
,您需要检查整个列表是否为空。但是您已经假设它不是空的并将其分解为a:b
. 这会导致您的代码 (a) 返回错误的结果,因为它丢弃了应该压缩的最后一对元素,并且 (b) 当其中一个参数是空列表时产生错误。
列表上的递归通常看起来更像这样:
myzip :: [a] -> [b] -> [(a, b)]
myzip [] _ = []
myzip _ [] = []
myzip (a:b) (z:g) = (a, z) : myzip b g
这行为正确。