3

第一次发帖,但这个网站对我帮助很大。

我正在努力学习 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')]'

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

感谢帮助,如果得到回答,可能会有更多问题。非常感谢您!

4

1 回答 1

10

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

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

您的函数需要采用这种类型,并返回列表第二个元素的第一个组件。元组的所有第一个组件都有 type 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 : []where构造一个包含三个元素的列表xy并且z它们都是类型a(请记住,运算符:将项目添加到列表的前面)。所以我们可以写:

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

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

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

我们的模式现在说“匹配三个元素的列表,然后是其他任何元素。” 事实上,我们可以让它变得更简单。我们不会使用 values xy或者我们可以用 Haskell 模式(下划线)rest替换它们。_这匹配任何东西,并让我们保证我们不会使用该值:

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

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

于 2012-06-11T18:45:55.473 回答