我开始使用haskell进行编程。我正在开发的程序只是将一个列表的总和与两个元素相加,例如:
[("book",10),("cookies",2),("icecream",5)]
这应该返回“17”。这是我的代码:
total [] = []
total ([("c",e)]:y) = total y ++ [e]
但是在GHCi中运行时,它给了我这个错误:
<interactive>:80:8:
Couldn't match expected type `[([Char], a0)]'
with actual type `([Char], t0)'
In the expression: ("livro", 10)
In the first argument of `total', namely
`[("livro", 10), ("bolachas", 2), ("gelado", 5)]'
In the expression:
total [("livro", 10), ("bolachas", 2), ("gelado", 5)]
<interactive>:80:21:
Couldn't match expected type `[([Char], a0)]'
with actual type `([Char], t1)'
In the expression: ("bolachas", 2)
In the first argument of `total', namely
`[("livro", 10), ("bolachas", 2), ("gelado", 5)]'
In the expression:
total [("livro", 10), ("bolachas", 2), ("gelado", 5)]
<interactive>:80:36:
Couldn't match expected type `[([Char], a0)]'
with actual type `([Char], t2)'
In the expression: ("gelado", 5)
In the first argument of `total', namely
`[("livro", 10), ("bolachas", 2), ("gelado", 5)]'
In the expression:
total [("livro", 10), ("bolachas", 2), ("gelado", 5)]
这可能非常简单,但作为初学者,我无法解决这个问题。
答案 0 :(得分:6)
在行中:
total ([("c",e)]:y) = total y ++ [e]
([("c",e)]:y)
没有做你想做的事。它匹配一个非空列表,其中第一个元素也是一个列表(因为[...]
),并且该子列表中只有一个元素,即第一个元素等于"c"
的对。为了匹配你想要的东西,你需要写:
total ((c,e):y) = total y ++ [e]
然而,这仍然不会做你想要的,因为它构造了输入列表中所有e
值的列表。要将它们相加,您需要这样做:
total [] = 0
total ((c,e):y) = total y + e
答案 1 :(得分:4)
除了@jwodder所说的,还要注意还有另一种解决问题的方法。不要考虑/如何/你将计算所需的值,考虑/你做什么:你采取每个列表项的第二个元素,然后将这些元素加起来。
所以你可以从编写两个函数开始,一个函数获取一个元组列表,产生一个包含所有第二个元素的列表,另一个函数计算给定数字列表的总和。一个好的开始是提出类型签名,但定义要评估为undefined
的函数:
-- Takes a list of tuples and returns all the second elements
getSecondElements :: [(String, Int)] -> [Int]
getSecondElements someList = undefined
-- Computes the sum of a given list of integers
sumOfIntList :: [Int] -> Int
sumOfIntList someInts = undefined
使用这些,定义你的功能很简单:
total myList = sumOfIntList (getSecondElements myList)
你可以通过ghci运行它并进行类型检查,这是一个好兆头。当您尝试实际使用total
时,会收到错误,因为其他两个函数只是undefined
。
在你开始思考如何实施它们之前,最好先看看它们是否已经存在。您可以搜索Hoogle类型签名,并搜索与该签名匹配的函数。 getSecondElements
的签名不会产生任何点击,但第二个的签名会产生很多点击,而且大多数点击甚至根本不会谈论Int
:Hoogle足够聪明,可以理解处理任意类型的列表(例如:length
或head
)的函数也可能适用。如果您向下滚动页面,您会发现现有的sum
功能已经存在!
对于第一个函数,您可以重复该过程(如果您愿意,可以递归地):为了获取列表中的所有二元组元素,您首先需要一个函数来获取元组的第二个元素,如
getSecondElement :: (String, Int) -> Int
getSecondElement = undefined
和另一个将其应用于列表的所有元素的函数。我稍后跳过:获取2元组的第二个元素的标准函数称为snd
,并且用于收集在列表的所有元素上调用函数的结果的函数被调用map
。尝试运行
:t map snd
在ghci中查看map snd
的类型:
map snd :: [(a, b)] -> [b]
...这是我们getSecondElements
函数类型的通用版本!所以这两个缺失的部分是map snd
和sum
,它们给出了:
-- Takes a list of tuples and returns all the second elements
getSecondElements :: [(String, Int)] -> [Int]
getSecondElements someList = map snd someList
-- Computes the sum of a given list of integers
sumOfIntList :: [Int] -> Int
sumOfIntList someInts = sum
您可以直接使用total
和map snd
来定义sum
,而不是拥有两个额外的功能:
total someList = sum (map snd someList)
......可以缩短为
total = sum . map snd
不错,是吗?