为什么maxBy只返回单项?

时间:2011-11-22 19:31:17

标签: scala collections

跟进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的任何标准实现,它会返回一系列已找到的项目?

4 个答案:

答案 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次。