如何在scalaz.Foldable容器中查找所有最大元素

时间:2015-03-27 03:15:38

标签: scalaz

scalaz.Foldable有一个maximumBy方法,可以在容器中找到 a 最大元素。但有没有一种优雅的方法来使用scalaz找到它们所有?即:

Vector(Person("Ben", 1), Person("Jil", 3), Person("Bob", 3)).maximumsBy(_.age) 
== Vector(Person("Jil", 3), Person("Bob", 3))

我有一个问题,如果有几个相等的最大值,我想随机选择这些候选者。

2 个答案:

答案 0 :(得分:2)

你可以做那样的事情

implicit def MaxNonEmptyListSemigroup[A : Order]: 
  Semigroup[NonEmptyList[A]] = new Semigroup[NonEmptyList[A]] {
  def append(l1: NonEmptyList[A], l2: =>NonEmptyList[A]): NonEmptyList[A] =
    Order[A].apply(l1.head, l2.head) match {
      case GT => l1
      case LT => l2
      case EQ => l1 append l2
    }
}

// returns None if the list is empty
// otherwise returns Some(non-empty-list of maximum elements)
list.foldMap1Opt(a => NonEmptyList.nels(a)) :: Option[NonEmptyList[A]]

答案 1 :(得分:0)

理想情况下,maximumsBy将返回与提供的容器相同类型的最大值。为了有效地做到这一点,似乎需要scalaz.Reducer,一个类型化模型,用于对容器进行追加和前置建模。

import scalaz._
import Ordering._
import std.AllInstances._

object Maximums extends App {

  def maximumsBy[F[_]: Foldable, A, B: Order](fa: F[A])(f: A => B)
                (implicit r: Reducer[A, F[A]]): Option[F[A]] =
    Foldable[F].foldMapLeft1Opt(fa)(a => (f(a), r.unit(a))) {
      case (curr@(max, maxes), a) => {
        val next = f(a)
        Order[B].apply(next, max) match {
          case GT => (next, r.unit(a))
          case LT => curr
          case EQ => (max, r.snoc(maxes, a))
        }
      }
    }.map(_._2)

  println(maximumsBy(Vector(("a", 1), ("c", 3), ("c", 3)))(_._2))
  println(maximumsBy(List(("a", 1), ("c", 3), ("c", 3)))(_._2))
  //Output:
  //Some(Vector((c,3), (c,3)))
  //Some(List((c,3), (c,3)))
}

我对于maximumsBy最终的复杂程度感到有些沮丧。有没有办法简化它,同时保持相同的行为?