我尝试编写一个函数,它接受一个子列表列表,反转子列表并返回连接的反向子列表。这是我的尝试:
conrev :: Ord a => [[a]] -> [a]
conrev [[]] = []
conrev [[a]] = reverse [a]
conrev [(x:xs)] = reverse x ++ conrev [xs]
main = putStrLn (show (conrev [[1,2],[],[3,4]]))
我收到此错误:
3.hs:4:27:
Could not deduce (a ~ [a])
from the context (Ord a)
bound by the type signature for conrev :: Ord a => [[a]] -> [a]
at 3.hs:1:11-31
`a' is a rigid type variable bound by
the type signature for conrev :: Ord a => [[a]] -> [a] at 3.hs:1:11
In the first argument of `reverse', namely `x'
In the first argument of `(++)', namely `reverse x'
In the expression: reverse x ++ conrev [xs]
我做错了什么?第二个问题是 - 类型签名可能更通用吗?我必须写得尽可能通用。
答案 0 :(得分:6)
在等式中
conrev [(x:xs)] = reverse x ++ conrev [xs]
匹配包含单个元素的列表,该列表是非空列表x:xs
。所以,给定类型
conrev :: Ord a => [[a]] -> [a]
列表x:xs
必须包含[a]
类型,因此x :: a
。
现在,您拨打reverse x
,这意味着x
必须是列表x :: [b]
。然后你连接
reverse x :: [b]
与
conrev [xs] :: [a]
从开始,b
必须与a
的类型相同。但早先确定a ~ [b]
。总而言之,这个等式需要a ~ [a]
。
如果您没有编写(不必要的)Ord a
约束,那么您会得到不太透明的
Couldn't construct infinite type a = [a]
错误。
如果您移除了一些外部[]
:
conrev :: Ord a => [[a]] -> [a]
conrev [] = []
conrev [a] = reverse a
conrev (x:xs) = reverse x ++ conrev xs
但更好的实施将是
conrev = concat . map reverse
答案 1 :(得分:3)
你的第二种模式与你想要的不匹配,看起来你错误地认为了价值结构的类型结构。
[[a]]
作为类型表示“某种类型的列表列表a
”
[[a]]
作为模式意味着“匹配包含单个列表的列表,其中包含单个元素,该元素将绑定到名称< / em> a
。
编辑: 如果我理解你要做的事情,那么中间案例实际上是多余的。第三种情况将处理非空列表,第一种情况将处理空列表。为单身人士名单制作另一个案例是不必要的。
编辑2:
第三种情况的实施存在进一步的问题。
conrev :: Ord a => [[a]] -> [a]
conrev [(x:xs)] = reverse x ++ conrev [xs]
根据您看到的类型x
必须是[a]
类型而xs
必须是[[a]]
类型。因此,写conrev [xs]
会将[[[a]]]
类型的值传递给conrev
。这是您的类型错误来自的地方。您通过调用[a]
隐含地声明a
与convrev [xs]
的类型相同。