Scala 最小值/最大值,带选项[T],用于可能为空的序列?

Scala 最小值/最大值,带选项[T],用于可能为空的序列?,scala,scala-collections,Scala,Scala Collections,我正在做一点Scala体操,在这里我尝试找到“最小”的元素。这就是我现在所做的: val leastOrNone = seq.reduceOption { (best, current) => if (current.something < best.something) current else best } 。。。但是min和minBy在序列为空时抛出异常。是否有一种惯用的、更优雅的方法来查找可能为空的列表中的最小元素作为选项 在Haskell中,您可以将呼叫包

我正在做一点Scala体操,在这里我尝试找到“最小”的元素。这就是我现在所做的:

val leastOrNone = seq.reduceOption { (best, current) =>
    if (current.something < best.something) current
    else best
}
。。。但是
min
minBy
在序列为空时抛出异常。是否有一种惯用的、更优雅的方法来查找可能为空的列表中的最小元素作为
选项

在Haskell中,您可以将呼叫包装为

这个怎么样

import util.control.Exception._
allCatch opt seq.minBy(_.something)
或者,更详细地说,如果您不想接受其他异常:

catching(classOf[UnsupportedOperationException]) opt seq.minBy(_.something)
或者,您可以使用以下内容来拉皮条:

import collection._

class TraversableOnceExt[CC, A](coll: CC, asTraversable: CC => TraversableOnce[A]) {

  def minOption(implicit cmp: Ordering[A]): Option[A] = {
    val trav = asTraversable(coll)
    if (trav.isEmpty) None
    else Some(trav.min)
  }

  def minOptionBy[B](f: A => B)(implicit cmp: Ordering[B]): Option[A] = {
    val trav = asTraversable(coll)
    if (trav.isEmpty) None
    else Some(trav.minBy(f))
  }
}

implicit def extendTraversable[A, C[A] <: TraversableOnce[A]](coll: C[A]): TraversableOnceExt[C[A], A] =
  new TraversableOnceExt[C[A], A](coll, identity)

implicit def extendStringTraversable(string: String): TraversableOnceExt[String, Char] =
  new TraversableOnceExt[String, Char](string, implicitly)

implicit def extendArrayTraversable[A](array: Array[A]): TraversableOnceExt[Array[A], A] =
  new TraversableOnceExt[Array[A], A](array, implicitly)
导入集合_
类TraversableOnceText[CC,A](coll:CC,asTraversable:CC=>TraversableOnce[A]){
def minOption(隐式cmp:排序[A]):选项[A]={
val trav=可转换(coll)
如果(trav.isEmpty)没有
其他一些(trav.min)
}
def minOptionBy[B](f:A=>B)(隐式cmp:Ordering[B]):选项[A]={
val trav=可转换(coll)
如果(trav.isEmpty)没有
其他一些(trav.minBy(f))
}
}

implicit def extendTraversable[A,C[A]由于
O(nlogn)
复杂性,对于任何更大的列表,几乎都不是一个选项:

seq.sortBy(_.something).headOption
你想要什么


编辑:下面是一个包含您的
\的示例

case class Foo(a: Int, b: Int)
val seq = Seq(Foo(1,1),Foo(2,0),Foo(0,3))
val ord = Ordering.by((_: Foo).b)
seq.reduceOption(ord.min)  //Option[Foo] = Some(Foo(2,0))
或者,作为一般方法:

def minOptionBy[A, B: Ordering](seq: Seq[A])(f: A => B) = 
  seq reduceOption Ordering.by(f).min

您可以使用
minOptionBy(seq)(.something)

安全、紧凑且
O(n)
的Scalaz版本调用它:

xs.nonEmpty option xs.minBy(_.foo)

我以前也遇到过同样的问题,所以我扩展了Ordered并实现了compare函数。 下面是一个例子:

 case class Point(longitude0: String, latitude0: String)  extends Ordered [Point]{

  def this(point: Point) = this(point.original_longitude,point.original_latitude)
  val original_longitude = longitude0
  val original_latitude = latitude0

  val longitude = parseDouble(longitude0).get 
  val latitude = parseDouble(latitude0).get  

  override def toString: String = "longitude: " +original_longitude +", latitude: "+ original_latitude

  def parseDouble(s: String):  Option[Double] = try { Some(s.toDouble) } catch { case _ => None }

  def distance(other: Point): Double =
    sqrt(pow(longitude - other.longitude, 2) + pow(latitude - other.latitude, 2))

 override def compare(that: Point): Int = {
  if (longitude < that.longitude)
    return -1
  else if (longitude == that.longitude && latitude < that.latitude)
    return -1
  else
    return 1
 }
}

从Scala 2.13开始,/
maxByOption
现在是标准库的一部分,如果序列为空,则返回
None

seq.minByOption(_.something)
List((3,'a'),(1,'b'),(5,'c'))。minByOption(u._1)//Option[(Int,Char)]=一些((1,b))
List[(Int,Char)]()。minByOption(u.\u 1)//Option[(Int,Char)]=None

您可以随时执行以下操作:

case class Foo(num: Int)

val foos: Seq[Foo] = Seq(Foo(1), Foo(2), Foo(3))
val noFoos: Seq[Foo] = Seq.empty

def minByOpt(foos: Seq[Foo]): Option[Foo] =
  foos.foldLeft(None: Option[Foo]) { (acc, elem) => 
    Option((elem +: acc.toSeq).minBy(_.num)) 
  }
然后使用类似于:

scala> minByOpt(foos)
res0: Option[Foo] = Some(Foo(1))

scala> minByOpt(noFoos)
res1: Option[Foo] = None
对于小于2.13的scala

Try(seq.minBy(_.something)).toOption
对于scala 2.13

seq.minByOption(_.something)

此外,也可以这样做

Some(seq).filter(_.nonEmpty).map(_.minBy(_.something))

首先,我要避免异常——使用防护来排除异常发生的可能性。这在语义上比依靠异常语义为您提供一个可选值更简洁。毫无疑问,Haskell很酷。但“这是在另一种编程语言中可以做到的”没有回答问题。-1相当于一个空的catch块。抛出的任何其他异常也将被吞没。也可以使用
seq reduceOption math.min
。这更有效,因为它不需要隐式转换。@Antoras的想法不错,但我相信任何差异实际上都是经过优化的至少,这就是我的微型本马克展示的奥卡姆剃须刀。它的碎片锋利而危险!问题是要安全地找到最大值,而不是对列表进行排序。@fatuhoku从数学上(因此是纯FP?)得出的结论从观点来看,
sortBy
+
headOption
似乎相当于
minByOpt
-为什么它会弄坏奥卡姆的剃须刀?@ericalik Hmmm。不知道我为什么写这个。我显然不知道几年前奥卡姆的剃须刀到底是什么。可耻!现在我对这样的答案有了更多的欣赏,尽管有accepted答案是一个更好的主意。这如何解决空集合上的min和max抛出异常的问题?
scala> minByOpt(foos)
res0: Option[Foo] = Some(Foo(1))

scala> minByOpt(noFoos)
res1: Option[Foo] = None
Try(seq.minBy(_.something)).toOption
seq.minByOption(_.something)
Some(seq).filter(_.nonEmpty).map(_.minBy(_.something))