跟进this question我想知道为什么<{em} maxBy
Traversable[T]
会返回单个值T
而不是T
的序列(列表或类似)。这看起来很常见。例如(来自上一个问题):
有关成绩的学生列表
List(Student("Mike", "A"), Student("Pete", "B"), Student("Paul", A))"
我想
List(Student("Mike", "A"), Student("Paul", A))
是否有人知道maxBy
的任何标准实现,它会返回一系列已找到的项目?
答案 0 :(得分:6)
没有单一命令。我所知道的最短 - 将所有内容分组,而不仅仅是作为中间人的最大值 - 是
xs.groupBy(f).maxBy(_._1)._2
为了提高效率,折叠是用于查找总和和最大值以及各种类似事物的通用工具。基本上,任何时候你需要在积累一些答案时跑过你的收藏,使用折叠。在这种情况下,
(xs.head /: xs.tail) {
(biggest, next) => if (f(biggest) < f(next)) next else biggest
}
如果您不介意为每个元素重新评估该函数两次,将执行maxBy(f)
,而
((xs.head, f(xs.head)) /: xs.tail) {
case (scored, next) =>
val nextscore = f(next)
if (scored._2 < nextscore) (next, nextscore)
else scored
}._1
只对每个元素进行一次评估。如果要保留序列,可以将其修改为
(Seq(xs.head) /: xs.tail) {
(bigs, next) =>
if (f(bigs.head) > f(next)) bigs
else if (f(bigs.head) < f(next)) Seq(next)
else bigs :+ next
}
保留列表(相应的单一评估表格留给读者练习。)
最后,如果您愿意使用一些可变变量(希望隐藏在像我这样的代码块中),即使是接近最大效率的版本也不是 难以管理的所有在这里)
val result = {
var bigs = xs.take(0).toList
var bestSoFar = f(xs.head)
xs.foreach{ x =>
if (bigs.isEmpty) bigs = x :: bigs
else {
val fx = f(x)
if (fx > bestSoFar) {
bestSoFar = fx
bigs = List(x)
}
else if (fx == bestSoFar) bigs = x :: bigs
}
}
bigs
}
(顺便说一下,这将以相反的顺序返回)。
答案 1 :(得分:3)
我所知道的标准库中没有任何功能。
maxBy' :: (a -> a -> Ordering) -> [a] -> [a]
maxBy' _ [] = undefined
maxBy' f (x:xs) = foldr step [x] xs
where step y acc@(z:_) = case f y z of
GT -> [y]
EQ -> y:acc
LT -> acc
[edit]哎呀,这是一个Scala问题:)
转换为Scala,给出一个列表xs
和一个比较器compare
:
(List(xs.head) /: xs.tail) { (acc, y) =>
y compare acc.head match {
case 1 => List(y)
case 0 => y :: acc
case -1 => acc
}
}
答案 2 :(得分:3)
如果你有
case class Student(name: String, grade: String)
val students = List(Student("Mike", "A"), Student("Pete", "B"), Student("Paul", "A"))
那么这是一个非常简单的O(N)解决方案,它不涉及构建任何中间列表:
val bestGrade = students.minBy(_.grade).grade
students.filter(_.grade == bestGrade) //List(Student(Mike,A), Student(Paul,A))
由于字符串的排序,我们在这里使用minBy
。
作为一种方法:
def multiMinBy[A,B](xs: Traversable[A])(f: A => B)(implicit ord: Ordering[B]) = {
val minVal = f(xs minBy f)
xs filter (f(_) == minVal)
}
scala> multiMinBy(students)(_.grade)
res26: Traversable[Student] = List(Student(Mike,A), Student(Paul,A))
答案 3 :(得分:1)
学生和学生名单:
class Student (val name: String, val grade: String) {
override def toString = grade + "::" + name
}
val students = List (new Student ("Mike", "A"), new Student ("Pete", "B"), new Student ("Paul", "A"))
功能性,tairecursive解决方案,参数化T列表和比较2 T的方法:
// ext: extreme, o: other, s:sample(student)
@tailrec
def collectExtreme [T](l: List[T], ext: ((T, T) => Int), carry: List[T]=List.empty) : List[T] =
l match {
case Nil => carry
case s :: xs => carry match {
case Nil => collectExtreme (xs, ext, List (s))
case o :: _ => ext (s, o) match {
case 0 => collectExtreme (xs, ext, s :: carry)
case -1=> collectExtreme (xs, ext, l)
case 1 => collectExtreme (xs, ext, carry)
}
}
}
def cmp (s: Student, o: Student): Int = s.grade(0) - o.grade(0)
collectExtreme (students, cmp)
也只在集合上运行1次。