Scala 如何在列表中找到成绩最好的学生?
假设我有一个Scala 如何在列表中找到成绩最好的学生?,scala,list-comprehension,Scala,List Comprehension,假设我有一个学生的列表Students有name,出生日期,成绩等字段。您如何找到Scala中成绩最好的Students 例如: List(Student("Mike", "A"), Student("Pete", "B"), Student("Paul", A))" 此解决方案是O(N),但会在列表上运行两次。你能推荐一个更好的解决方案吗?你可以在学生名单上使用过滤器 case class Student(grade: Int) val students = ... val minGrade
学生的列表
Students
有name
,出生日期
,成绩
等字段。您如何找到Scala中成绩最好的Students
例如:
List(Student("Mike", "A"), Student("Pete", "B"), Student("Paul", A))"
此解决方案是
O(N)
,但会在列表上运行两次。你能推荐一个更好的解决方案吗?你可以在学生名单上使用过滤器
case class Student(grade: Int)
val students = ...
val minGrade = 5
students filter ( _.grade < minGrade)
案例班学生(年级:Int)
val学生=。。。
val minGrade=5
学生筛选(u.年级
同样适用于类型String
运行列表两次可能是最好的方法,但是如果您坚持只运行一次的解决方案,您可以使用折叠(此处适用于空列表):
如果您不熟悉褶皱,请在回答中描述它们。它们是当你经过一个集合(例如列表)时积累东西的一般方式。如果您不熟悉匹配,可以使用isEmpty
和head
执行相同的操作。如果您希望学生的顺序与他们在原始列表中出现的顺序相同,请在最后运行。反转。排序后,您可以使用takeWhile避免第二次迭代
case class Student(grade: Int)
val list = List(Student(1), Student(3), Student(2), Student(1), Student(25), Student(0), Student (25))
val sorted = list.sortWith (_.grade > _.grade)
sorted.takeWhile (_.grade == sorted(0).grade)
在吃鲜奶油之前,它仍然会对整个事情进行排序,即使是我们不感兴趣的1、3、0和-1级,但代码很短
更新:
第二种方法可以并行执行,即拆分列表,并取每侧的最大值,然后仅取较高的值(如果有),否则两者均为:
def takeMax (l: List[Student]) : List [Student] = l.size match {
case 0 => Nil
case 1 => l
case 2 => if (l(0).grade > l(1).grade) List (l(0)) else
if (l(0).grade < l(1).grade) List (l(1)) else List (l(0), l(1))
case _ => {
val left = takeMax (l.take (l.size / 2))
val right= takeMax (l.drop (l.size / 2))
if (left (0).grade > right(0).grade) left else
if (left (0).grade < right(0).grade) right else left ::: right }
}
使用foldLeft只能遍历学生列表一次:
scala> students.foldLeft(List.empty[Student]) {
| case (Nil, student) => student::Nil
| case (list, student) if (list.head.grade == student.grade) => student::list
| case (list, student) if (list.head.grade > student.grade) => student::Nil
| case (list, _) => list
| }
res17: List[Student] = List(Student(Paul,A), Student(Mike,A))
已经有6个答案,但我仍然觉得有必要补充我的观点:
case class Lifted(grade: String, students: List[Student]) {
def mergeBest(other: Lifted) = grade compare other.grade match {
case 0 => copy(students = other.students ::: students)
case 1 => other
case -1 => this
}
}
这个小案例类将把一个学生提升到一个跟踪最佳成绩的对象和一个包含该学生的列表单元格中。它还考虑了合并的逻辑:如果新学生
- 同一等级=>将新学生合并到结果列表中,较短的学生在前面以提高效率-否则结果不会是O(n)
- 高分=>用新学生替换当前最佳成绩
- 低年级=>保持当前最佳成绩
然后可以使用reduceLeft
轻松构造结果:
val result = {
list.view.map(s => Lifted(s.grade, List(s)))
.reduceLeft((l1, l2) => l1.mergeBest(l2))
.students
}
// result: List[Student] = List(Student(Paul,A), Student(Mike,A))
另外,我对你的问题投了更高的票——根据生成的回复数量来看,“最好的分数”是模糊的。你想要什么?前十名?前10%?分数在X以上?@Kim,无论什么给他最好的分数,这肯定是:)我的建议是忘记这个O(N)的事情,并担心你有多少次通过了名单。除非你的班上有数百万学生,而且这个方法对时间很关键,否则就使用最简单的代码来完成你想要的。它一般不适用于字符串。例如“10”<“2”
是真的。@LuigiPlinge当然,谢谢。我想到了他的例子,将分数表示为单个字符。+1表示分解策略(尽管由于列表具有线性访问,所以不太可能取得性能胜利;这仍然是一个好主意)。
def takeMax [A] (l: List[A], cmp: ((A, A) => Int)) : List [A] = l.size match {
case 0 | 1 => l
case 2 => cmp (l(0), l(1)) match {
case 0 => l
case x => if (x > 0) List (l(0)) else List (l(1))
}
case _ => {
val left = takeMax (l.take (l.size / 2), cmp)
val right= takeMax (l.drop (l.size / 2), cmp)
cmp (left (0), right (0)) match {
case 0 => left ::: right
case x => if (x > 0) left else right }
}
}
def cmp (s1: Student, s2: Student) = s1.grade - s2.grade
takeMax (list, cmp)
scala> students.foldLeft(List.empty[Student]) {
| case (Nil, student) => student::Nil
| case (list, student) if (list.head.grade == student.grade) => student::list
| case (list, student) if (list.head.grade > student.grade) => student::Nil
| case (list, _) => list
| }
res17: List[Student] = List(Student(Paul,A), Student(Mike,A))
case class Lifted(grade: String, students: List[Student]) {
def mergeBest(other: Lifted) = grade compare other.grade match {
case 0 => copy(students = other.students ::: students)
case 1 => other
case -1 => this
}
}
val result = {
list.view.map(s => Lifted(s.grade, List(s)))
.reduceLeft((l1, l2) => l1.mergeBest(l2))
.students
}
// result: List[Student] = List(Student(Paul,A), Student(Mike,A))