我有一个自定义数据类型Movie = String Int [(String,Int)]
(电影名称年份[(粉丝,评分)],并希望做一些事情:
首先,我想创建一个函数,从元组列表中平均Ints,并输出该数字。到目前为止,我有这个不完整的功能:
avgRating :: [DataType] -> Int
avgRating [(Movie a b [(fan,rating)])] = sumRatings / (length [<mylist>])
这里我需要一个函数sumRatings来递归列表并总结所有评级,但我不知道从哪里开始。
我在这里遇到的另一个问题是,我不确定将<mylist>
放在哪里,因为我通常会给列表一个变量名,然后在那里使用它,但是因为我将列表拆分了定义其他变量我无法命名。
我希望这是有道理的,谢谢。
答案 0 :(得分:4)
我猜你有一个定义为
的数据结构data Movie = Movie String Int [(String, Int)]
虽然这有效,但是当你拥有那么多字段时,使用它可能有点麻烦。相反,您可以利用类型别名并将语法记录为
type Name = String
type Year = Int
type Rating = Int
data Movie = Movie
{ mName :: Name
, mYear :: Year
, mRatings :: [(Name, Rating)]
} deriving (Eq, Show)
现在事情变得更加明确,更容易使用。 mName
,mYear
和mRatings
函数将使用Movie
并从中返回相应的字段。您的Movie
构造函数仍然以相同的方式工作,因此它不会破坏现有代码。
要计算评分的平均值,您真的需要一个能够提取电影所有评分的功能,并将它们汇总到一个列表中:
ratings :: Movie -> [Rating]
ratings mov = map snd $ mRatings mov
然后你只需要一个average
功能。这将有点不同,因为您无法直接计算Int
的平均值,您必须转换为浮点类型:
average :: [Rating] -> Float -- Double precision isn't really needed here
average rs = fromIntegral (sum rs) / fromIntegral (length rs)
fromIntegral
函数将Int
转换为Float
(实际的类型签名更为通用)。由于sum
的{{1}}是Int
且列表的Int
始终为length
,因此您需要转换这两者。
现在你可以把它们组成一个函数:
Int
现在,如果您需要计算多部电影的平均评分,可以将movieAvgRating :: Movie -> Float
movieAvgRating = average . ratings
应用于每部电影,将其汇总到一个评分列表中,然后在其上调用ratings
。我建议查看average
函数。你会想要制作像
concatMap
答案 1 :(得分:1)
要先回答第二个问题,您可以使用@
绑定到变量并同时将其解压缩:
avgRating [(Movie a b mylist@[(fan, rating)])] = …
另请注意,如果您不打算使用解包的变量,那么将Haskell约定绑定到_
:
avgRating [(Movie _ _ mylist@[(fan, rating)])] = …
这有助于读者关注真正重要的事情。
我不想只为您提供递归问题的解决方案,因为学习编写递归函数是Haskell编程的一个重要且有益的部分。 (如果你真的希望我为你破坏它,请在评论中告诉我。)然而,基本的想法是你需要考虑两种不同的情况:基本情况(递归停止的地方)和递归情况。例如,考虑内置的sum
函数:
sum :: Num a => [a] -> a
sum [] = 0
sum (x:xs) = x + sum xs
这里,基本情况是sum
得到一个空列表 - 它只是求值为0.在递归的情况下,我们假设sum
已经可以产生一个较小列表的总和,并且我们将其扩展到更大的列表。
如果你在一般的递归方面遇到麻烦,Harold Abelson和Gerald Jay Sussman将在Structure and Interpretation of Computer Programs,第二版,麻省理工学院出版社(剑桥),1996年开始详细讨论这个主题。 。 21(§§1.1.7–1.2)。它在Scheme中,而不是Haskell,但语言非常相似 - 至少在这个概念层面 - 每个语言都可以作为另一个的合适模型。