Haskell代码错误 - 我不明白它的要求

时间:2012-06-11 18:39:55

标签: haskell

第一次发布海报,但这个网站帮了我很多。

我正在努力学习Haskell。

这是我要回答的问题。

编写一个函数,该函数获取长度对的列表(长度> = 2)并返回列表中第二个元素的第一个组件。因此,当提供[(5,'b'),(1,'c'),(6,'a')]时,它将返回1.

我自己就是这样做的。

listtwo :: [([a],b)] -> [a]
listtwo [] = []
listtwo [(a,b)] = fst (head (tail [(a,b)]))

我正在尝试列出列表元组我相信并从列表中的第二个项目返回第一个元素。我知道如果你取出[(a,b)]并将第二个[(a,b)]替换为类似问题的列表,它可以正常工作。但是当我尝试使这个函数适用于任何元组列表时。我收到错误。

我收到错误

<interactive>:1:27:
No instance for (Num [a0])
arising from the literal `6'
Possible fix: add an instance declaration for (Num [a0])
In the expression: 6
In the expression: (6, 'a')
In the first argument of `listtwo', namely
  `[(5, 'b'), (1, 'c'), (6, 'a')]'

所以我问是否有人可以帮助我解决错误并且解释我做错了什么(不要给我答案,不能这样学习)。

如果能得到答案,请提供帮助,可能会有更多问题。非常感谢你提前!

1 个答案:

答案 0 :(得分:10)

你说你想要一个函数来返回列表的第二个元素的第一个组件。编写此函数的最佳方法是通过模式匹配。但首先,让我们考虑它的类型。

假设您需要(Int,Char)的元组列表。这写为[(Int,Char)]。如果你想要一个任意类型的2元组列表,你可以用类型变量替换IntChar类型,这样你最终会得到类型[(a,b)]

您的函数需要采用此类型的函数,并返回列表第二个元素的第一个组件。元组的所有第一个组件都有a类型,因此您的返回类型也必须为a。所以你的函数的类型签名是

f :: [(a,b)] -> a

现在,我们如何编写这个函数?最好的方法是使用模式匹配。这是一种提取数据结构组件的简洁方法,无需使用访问器(如果您来自面向对象的背景,也称为getter)。假设我们有一个函数g :: [a] -> a,它返回列表的第三个组件。你可以写

g :: [a] -> a
g xs = head (tail (tail xs))

但这看起来很讨厌。另一种方式是模式匹配。可以通过[x,y,z]来构建包含三个元素x : y : z : []的列表,其中xyz都是a类型(请记住运算符:将项添加到列表的前面。所以我们可以写:

g :: [a] -> a
g (x : y : z : []) = z

但是这有一个问题 - 它只适用于长度为3的列表,因为我们的模式说“将三个元素的列表与最后添加的空列表匹配”。相反,我们可以使用模式x : y : z : rest,现在rest匹配列表的其余部分:

g :: [a] -> a
g (x : y : z : rest) = z

我们的模式现在显示“匹配三个元素的列表,后跟任何其他元素。”事实上,我们可以使它更简单。我们不会使用值xyrest,因此我们可以使用Haskell模式_(下划线)替换它们。这匹配任何东西,并使我们承诺我们不会使用该值:

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

我们如何使用它来解决您的问题?好吧,如果你有一个匹配模式(w,x) : (y,z) : rest的列表,你会想要返回y。所以你可以写:

f :: [(a,b)] -> a
f ( (w,x) : (y,z) : rest ) = y

哪个会好的。但是,您根本不关心第一对,因此您可以将(w,x)替换为_。您也不关心第二个元组的第二个元素或列表的其余部分,因此您也可以用_替换它们,获取:

f :: [(a,b)] -> a
f ( _ : (y,_) : _) = y

在ghci中检查:

ghci> f [(5,'b'),(1,'c'),(6,'a')]
1

所以它的行为与你预期的一样。