【问题标题】:Recursion over Lists - Haskell递归列表 - Haskell
【发布时间】:2019-07-21 12:16:56
【问题描述】:

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

listMarks :: String -> [StudentMark] -> [Int]

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

listMarks "Joe" [("Joe", 45), ("Sam", 70), ("Joe", 52)] = [45,52]

这是我编写函数的方式:

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

如果列表中的字符串与“std”字符串不同,这将不起作用。我想了解为什么以及如何进行这项工作?谢谢!

【问题讨论】:

    标签: list haskell recursion tuples


    【解决方案1】:

    轻松修复

    只需更换警卫| 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
    

    更好的版本

    如您所见,您总是使用相同的第一个参数调用该函数,因此您很可能可以编写一个更简洁的版本。这里有两个快速的想法:

    列表理解

    我个人最喜欢的列表推导非常通用且清晰:

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

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

    高阶函数

    使用高阶函数mapfilterfold,你可以做的和递归和lcs 一样多,但通常看起来更整洁。您想再次根据第一个元素过滤列表,然后返回第二个。

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

    【讨论】:

    • 我看不到那个错误吗?太明显了啊,非常感谢!
    • 我认为filter 的 lambda 也可以设为无点:((== str) . fst)。这是否更具可读性是有争议的。 (警告:我实际上并没有尝试编译)
    猜你喜欢
    • 1970-01-01
    • 2013-01-17
    • 2011-07-16
    • 2015-03-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-02-20
    • 1970-01-01
    相关资源
    最近更新 更多