列表递归 - Haskell

Nic*_*kor 2 recursion haskell tuples list

基本上我有这个练习:回忆一下上周的StudentMark类型同义词.写一个递归函数:

listMarks :: String -> [StudentMark] -> [Int]
Run Code Online (Sandbox Code Playgroud)

它给出了特定学生的分数列表; 例如:

listMarks "Joe" [("Joe", 45), ("Sam", 70), ("Joe", 52)] = [45,52]
Run Code Online (Sandbox Code Playgroud)

这就是我编写函数的方式:

type StudentMark = (String, Int)
listMarks :: String -> [StudentMark] -> [Int]
listMarks _ [] = []
listMarks std (x:xs)
  | std == fst x = snd x : listMarks (fst x) xs
  | otherwise = listMarks (fst x) xs
Run Code Online (Sandbox Code Playgroud)

如果列表中的字符串与"std"字符串不同,则不起作用.我想了解为什么以及如何才能完成这项工作?谢谢!

Lor*_*nzo 7

轻松修复

只需改变警卫| otherwise = listMarks std xs.我也会在上面的守卫中改变它,因为| std == fst x = snd x : listMarks std xs它们是相同的,但是它会让你更清楚你想要实现的目标.所以你的代码将是:

type StudentMark = (String, Int)
listMarks :: String -> [StudentMark] -> [Int]
listMarks _ [] = []
listMarks std (x:xs)
  | std == fst x = snd x : listMarks std xs
  | otherwise = listMarks std xs
Run Code Online (Sandbox Code Playgroud)

更好的版本

正如您所看到的,您使用始终相同的第一个参数调用该函数,因此您很可能编写一个更整洁的版本.这里有两个简单的想法:

列表理解

就我个人而言,列表理解是非常多才多艺和清晰的:

listMarks' :: String -> [StudentMark] -> [Int]
listMarks' str marks = [m |(n,m) <- marks, n==str]
Run Code Online (Sandbox Code Playgroud)

基本上,您根据第一个元素过滤列表,然后返回第二个元素.

高阶函数

随着高阶函数map,filter并且fold,你可以做尽可能多的递归和LCS,但往往看起来更整洁.您希望再次根据第一个元素过滤列表,然后返回第二个元素.

listMarks'' :: String -> [StudentMark] -> [Int]
listMarks'' str =  map snd . filter (\(n,_) -> n == str)
Run Code Online (Sandbox Code Playgroud)