如果我们查看类型,您的代码没有多大意义:
prefix [Int] -> [Int] -> Bool
prefix [] [] = []
prefix y (x:xs)
| x == y = prefix y xs
| otherwise = 0
由于这两个参数是列表 ([Int]),因此这意味着 y 是 [Int],x 是 Int,xs 是 [Int]。但是然后你比较x == y,你不能比较一个列表和一个元素。 (==) 定义为(==) :: Eq a => a -> a -> Bool。
这里还有其他问题:你在第一个子句中返回了一个列表,但是返回类型是Bool,后来你返回了0(同样应该是Bool)。
如果我们定义一个函数,我们首先需要为它定义一个特定的模型。什么时候列表 l1 是列表 l2 的前缀?如果 l1 是一个空列表,那么 l1 总是一个前缀,而不管第二个列表的值如何,所以:
prefix [] _ = True
如果l1是一个列表(即(x:xs)),那么它在两种情况下不是前缀:(1)如果l2 是一个空列表; (2) 如果 l2 的第一项((y:ys)) 中的y 不等于x,则:
prefix _ [] = False
prefix (x:xs) (y:ys) | x /= y = False
| otherwise = ...
现在的问题是如何处理prefix (x:xs) (y:ys),以防x == y。在那种情况下,我们在两个列表上递归,所以prefix (x:xs) (y:ys) == prefix xs ys的结果(仅在x == y的情况下),所以:
| otherwise = prefix xs ys
或现在完整:
prefix :: [Int] -> [Int] -> Bool
prefix [] _ = True
prefix _ [] = False
prefix (x:xs) (y:ys) | x /= y = False
| otherwise = prefix xs ys
我们可以进一步将表达式推广到Eq a => [a] -> [a] -> Bool,使其与任何类型a一起工作,这是一个Eq实例(所以在@987654351上定义了一个(==)实例@):
prefix :: Eq a => [a] -> [a] -> Bool
prefix [] _ = True
prefix _ [] = False
prefix (x:xs) (y:ys) | x /= y = False
| otherwise = prefix xs ys
我们也可以交换条件,因为通常正逻辑比负逻辑更容易理解:
prefix :: Eq a => [a] -> [a] -> Bool
prefix [] _ = True
prefix _ [] = False
prefix (x:xs) (y:ys) | x == y = prefix xs ys
| otherwise = False
现在我们可以进一步移除守卫,并使用(&&) :: Bool -> Bool -> Bool 代替:
prefix :: Eq a => [a] -> [a] -> Bool
prefix [] _ = True
prefix _ [] = False
prefix (x:xs) (y:ys) = x == y && prefix xs ys