刚开始学习Haskell和 我试图实现一个max函数来递归地找到列表的最大值
max' :: (Num b) => [a] -> b
max' [] = 0
max' (x:xs)
| x > max' xs = x
| otherwise = max' xs
但是在尝试编译时遇到错误
无法将预期类型'b'与实际类型'a'匹配 'a'是绑定的刚性类型变量 类型签名: 马克斯' :: forall b a。 (Num b,Num a)=> [a] - > b 在implementationFunctions.hs:5:1-34 'b'是绑定的刚性类型变量 类型签名: 马克斯' :: forall b a。 (Num b,Num a)=> [a] - > b 在implementationFunctions.hs:5:1-34
任何人都可以帮我理解什么是错的 ?
答案 0 :(得分:10)
[a]
将a
作为输入(基于签名):b
的列表。但是你返回b
。这意味着您已经写过 - 无论列表中的元素类型如何 - 我们可以选择任何类型Num b
作为我们想要的输出,只要它是Num
。但这没有意义。如果我们输入字符串列表,我们当然可以计算“最大字符串”(按字典顺序),但我们不能将其作为(>) :: Ord a => a -> a -> Bool
返回。
另一个问题是您使用Ord
功能(作为警卫)。但是,您没有在函数签名中指定输入元素的类型必须是b
类型类的实例。所以你无法比较这些元素。
最小修复是将输入类型限制为max' :: (Ord b, Num b) => [b] -> b
max' [] = 0
max' (x:xs)
| x > max' xs = x
| otherwise = max' xs
以及:
0
话虽如此,如果我们提供一个空列表,那么返回max []
是非常有意义的。这会导致max [-1]
实际上大于max'
的奇怪事实:通常我们期望超集的最大值大于或等于集合的最大值。
因此max' :: Ord b => [b] -> b
max' [] = error "Empty list"
max' [x] = x
max' (x:xs@(_:_))
| x > max' xs = x
| otherwise = max' xs
函数可能最好被看作是一个非总函数:一个函数,并非每个输入都会产生输出。在这种情况下,空列表不会。
我们可以将其重写为错误:
Maybe b
现在有三种模式:(1)空列表,(2)单列表列表,(3)至少包含两个元素的列表。
然而,编写错误并不总是处理非总函数的好方法,因为在类型签名中看不到函数是非整数的。另一个要做的就是使用 Nothing
作为返回类型。如果没有最大值,则为Just x
;如果有最大值,则为max' :: Ord b => [b] -> Maybe b
max' [] = Nothing
max' [x] = Just x
max' (x:xs@(_:_))
| y <- max' xs = max x y
| otherwise = Nothing
:
max' :: Ord b => [b] -> Maybe b
max' [] = Nothing
max' [x] = Just x
max' (x:xs@(_:_)) = fmap (max x) (max' xs)
或更短:
Prelude> max' []
Nothing
Prelude> max' [1,4,2,5]
Just 5
Prelude> max' [-3]
Just (-3)
例如:
[a]
答案 1 :(得分:1)
你的函数会获取一些东西并返回其中一个东西。但是函数的类型签名表示它需要一个b
的列表,并返回一个完全不同的东西max' :: (Num a) => [a] -> a
。这困扰了编译器。它无法将声明的类型签名与实际实现(即“类型检查”)进行协调。
要解决此问题,请使类型签名与实现匹配:
newlist = [100 if x == 0 else x for x in listzero]