如何找到两个Option [Int]的min()或max()

时间:2012-09-28 12:22:09

标签: scala max option min

您如何在下面找到minValue? 我有自己的解决方案,但想看看别人会怎么做。

val i1: Option[Int] = ...
val i2: Option[Int] = ...
val defaultValue: Int = ...
val minValue = ?

9 个答案:

答案 0 :(得分:8)

更新:我刚刚注意到我的解决方案和你的答案中的解决方案表现不一样 - 我读了你的问题,因为当有两个值时要求两个值的最小值,但在你的回答你是否有效地对待None,就好像它包含的值要大于(min)或更小(对于max)而不是其他任何值。

更具体一点:如果i1Some(1)i2None,我的解决方案将返回默认值,而您的解决方案将返回1.

如果您想要后一种行为,可以使用Option[A]的默认半群实例和Int的{​​{3}}。例如,在Scalaz 7中,你会写:

import scalaz._, Scalaz._

optionMonoid(Semigroup.minSemigroup[Int]).append(i1, i2) getOrElse defaultValue

或以下简写:

Tags.Min(i1) |+| Tags.Min(i2) getOrElse defaultValue

它不像下面的applicative functor解决方案那么干净,但如果这是你的问题,那就是你的问题。


这是一种更惯用的方式,不涉及创建额外的列表:

(for { x <- i1; y <- i2 } yield math.min(x, y)) getOrElse defaultValue

或等同地:

i1.flatMap(x => i2.map(math.min(x, _))) getOrElse defaultValue

你正在做的是将一个两位函数(min)“提升”到一个应用函子(Option)中。 tropical semigroup使用其应用程序构建器语法使其变得简单:

import scalaz._, Scalaz._

(i1 |@| i2)(math.min) getOrElse defaultValue

在这种情况下,标准库解决方案并不是那么优雅,但这是一个有用的抽象知识。

答案 1 :(得分:4)

我使用以下方法解决了类似的问题。我们处理两个选项都有值的特殊情况,否则我们使用API​​方法Option.orElse

val a: Option[Int]  = Some(10)
val b: Option[Int] = Some(20)
val c: Option[Int] = (a, b) match {
  case (Some(x), Some(y)) => Some(x min y)
  case (x, y) => x orElse y
}

答案 2 :(得分:3)

val minValue: Int = List(i1, i2).flatten.sorted.headOption getOrElse defaultValue

答案 3 :(得分:3)

我认为这就是你所追求的:

val minValue = List(i1, i2).flatten match {
  case Nil => defaultValue
  case xs => xs.min
}

我会避免使用sorted,因为排序需要的处理要多于简单地找到最大值或最小值(尽管在这种情况下可能没有太大区别)。

答案 4 :(得分:2)

您可以在表达式中使用模式,丢弃与模式不匹配的值。

(for (Some(x) <- List(None, Some(3))) yield x) max

不如List.flatten方法好。

答案 5 :(得分:1)

如果您想避免使用scala和map / for / getOrElse,可以执行以下操作:

val minValue = (i1, i2) match {
  case (Some(x), Some(y)) => math.min(x, y)
  case _ => defaultValue
}

答案 6 :(得分:1)

未提及的另一个选项是使用reduceLeftOption(根据需要使用math.maxmath.min):

val min = (first ++ second).reduceLeftOption(math.min).getOrElse(defaultValue)
scala> val first = Some(10)
first: Some[Int] = Some(10)

scala> val second: Option[Int] = None
second: Option[Int] = None

scala> val defaultMin = -1
defaultMin: Int = -1

scala> (first ++ second).reduceLeftOption(math.min).getOrElse(defaultMin)
res7: Int = 10

scala> val first: Option[Int] = None
first: Option[Int] = None

scala> (first ++ second).reduceLeftOption(math.min).getOrElse(defaultMin)
res8: Int = -1

scala> val first = Some(10)
first: Some[Int] = Some(10)

scala> val second = Some(42)
second: Some[Int] = Some(42)

scala> (first ++ second).reduceLeftOption(math.min).getOrElse(defaultMin)
res9: Int = 10

答案 7 :(得分:1)

我们可以将2个T作为&TOption的{​​{3}}运算符结合使用,这使我们可以使用++(很好地处理Iterable情况形成的空可迭代情况),并在必要时使用Option退回到默认值:

None/None

答案 8 :(得分:0)

tl;博士

您可以做到这一点,你需要定制优雅的使用 Semigroup实例:

import cats.kernel.Semigroup
import cats.instances.option._ // this import is for cats std option combiner
import cats.syntax.semigroup._

object Implicits {
 implicit val intMinSemigroup: Semigroup[Int] =
   (x: Int, y: Int) => math.min(x, y)

 implicit val intMaxSemigroup: Semigroup[Int] =
   (x: Int, y: Int) => math.max(x, y)
}
 
import Implicits.intMinSemigroup
// these are results for minSemigroup
// List((Some(1),Some(1),Some(2)), (Some(1),Some(1),None), (None,Some(2),Some(2)), (None,None,None))
//import Implicits.intMaxSemigroup
// these are results for maxSemigroup
// List((Some(1),Some(2),Some(2)), (Some(1),Some(1),None), (None,Some(2),Some(2)), (None,None,None))
   
for {
 maybeA <- Seq(Some(1), None)
 maybeB <- Seq(Some(2), None)
} yield (maybeA, maybeA |+| maybeB, maybeB)

如果你想用默认值替换 None 你可以使用 combine 两次:

val defaultValue: Int = 3
val optionMin = for {
  maybeA <- Seq(Some(1), None)
  maybeB <- Seq(Some(2), None)
} yield (maybeA |+| maybeB) |+| Some(defaultValue)
// List(Some(1), Some(1), Some(2), Some(3))

工作原理

简而言之,Semigroup[A]typeclass 用于组合相同类型的两个值 A 到类型 A 的一个值. 这里我们使用std猫OptionMonoid(它扩展Semigroup[Option[A]])这里的源代码:

class OptionMonoid[A](implicit A: Semigroup[A]) extends Monoid[Option[A]] {
  def empty: Option[A] = None
  def combine(x: Option[A], y: Option[A]): Option[A] =
    x match {
      case None => y
      case Some(a) =>
        y match {
          case None    => x
          case Some(b) => Some(A.combine(a, b))
        }
    }
}

我们看到,它发生在他自己和一切选项匹配,我们应该给他的工作是implicit A: Semigroup[A]。在我们的案例中,我们为 minmax 案例编写了两个不同的组合器:

object Implicits {
 implicit val intMinSemigroup: Semigroup[Int] =
   (x: Int, y: Int) => math.min(x, y)

 implicit val intMaxSemigroup: Semigroup[Int] =
   (x: Int, y: Int) => math.max(x, y)
}

因此,我们导入组合器(即 import Implicits.intMinSemigroup)并仅使用 cats.syntax.semigroupcombine 函数用作运算符 |+|

maybeA |+| maybeB

总而言之,您可以为任何类型(不仅是Int)和组合选项定义您的自定义半群导入一些cats语法实例后的这种类型。