正确使用两个Option实例的方法

时间:2015-11-10 17:24:29

标签: scala functional-programming scala-option

当我有一个Option[T]实例时,使用Tmap()等monadic操作对flatMap()执行任何操作都非常容易。这样我就不必进行检查以确定它是定义的还是空的,并且链操作一起最终得到结果Option[R]的{​​{1}}。

我的困难在于是否有类似的优雅方式在两个R实例上执行功能。

让我们举一个简单的例子,我有两个val Option[T]x类型y。如果两者都被定义,我想得到它们的最大值;如果只定义了一个,我想得到它们的最大值;如果没有定义,我想得到Option[Int]

如果没有在第一个None的{​​{1}}内进行大量isDefined次检查,会如何优雅地写出来?

8 个答案:

答案 0 :(得分:5)

您可以使用以下内容:

def optMax(op1:Option[Int], op2: Option[Int]) = op1 ++ op2 match {    
  case Nil => None  
  case list => list.max
}

或者更好一点:

def f(vars: Option[Int]*) = (for( vs <- vars) yield vs).max

@ jwvh,谢谢你的改进:

def f(vars: Option[Int]*) = vars.max

答案 1 :(得分:3)

通常,如果两个值都已定义,您将要执行某些操作。 在这种情况下,您可以使用for-comprehension:

val aOpt: Option[Int] = getIntOpt
val bOpt: Option[Int] = getIntOpt

val maxOpt: Option[Int] = 
    for {
        a <- aOpt
        b <- bOpt
    } yield max(a, b)

现在,您所描述的问题并不常见。如果定义了两个值,您希望执行某些操作,但如果只定义了其中一个值,则还要检索选项的值。

我只是使用上面的for-comprehension,然后将两个调用链接到orElse,以便在maxOpt变成None时提供替代值。

maxOpt orElse aOpt orElse bOpt

orElse的签名:

def orElse[B >: A](alternative: ⇒ Option[B]): Option[B]

答案 2 :(得分:2)

这是另一个fwiw:

import scala.util.Try
def maxOpt (a:Option[Int]*)= Try(a.flatten.max).toOption

它适用于n个参数(包括零参数)。

答案 3 :(得分:1)

模式匹配可以让人很容易掌握,但这可能不是最优雅的方式:

def maxOpt[T](optA: Option[T], optB: Option[T])(implicit f: (T, T) => T): Option[T] = (optA, optB) match {
    case (Some(a), Some(b)) => Some(f(a, b))
    case (None, Some(b)) => Some(b)
    case (Some(a), None) => Some(a)
    case (None, None) => None
}

你最终得到的结论是:

scala> maxOpt(Some(1), None)(Math.max)
res2: Option[Int] = Some(1)

一旦你有了这个建筑,阻止,你可以在for-comp或monadic操作中使用它。

答案 4 :(得分:1)

要获得maxOpt,您还可以使用一个使用Scalaz的应用程序(aOpt | @ | bOpt){max(_,_)}&amp;然后链接orElses @dcastro建议。

答案 5 :(得分:1)

我认为您希望结果为Some[Int]|None,而不是Int|None(否则返回类型必须为Any):

  def maxOption(opts: Option[Int]*) = {
    val flattened = opts.flatten
    flattened.headOption.map { _ => flattened.max }
  }

答案 6 :(得分:1)

实际上,Scala已经或多或少直接为你提供了这种能力。

scala> import Ordering.Implicits._
import Ordering.Implicits._

scala> val (a,b,n:Option[Int]) = (Option(4), Option(9), None)
a: Option[Int] = Some(4)
b: Option[Int] = Some(9)
n: Option[Int] = None

scala> a max b
res60: Option[Int] = Some(9)

scala> a max n
res61: Option[Int] = Some(4)

scala> n max b
res62: Option[Int] = Some(9)

scala> n max n
res63: Option[Int] = None

答案 7 :(得分:1)

Haskell-ish对此问题的看法是观察以下操作:

max, min :: Ord a => a -> a -> a
max a b = if a < b then b else a
min a b = if a < b then a else b

... 关联

max a (max b c) == max (max a b) c
min a (min b c) == min (min a b) c

因此,任何类型Ord a => a以及这些操作中的任何一个都是半群,这是一个可以构建可重用抽象的概念。

你正在处理Maybe(Haskell为&#34;选项&#34;),它增加了一个通用的&#34;中性&#34;基本a类型的元素(您希望max Nothing x == x作为法律保留)。这将带您进入 monoids ,这是半群的子类型。

Haskell semigroups library提供了Semigroup类型类和两种包装类型MaxMin,它们通常实现相应的行为。

由于我们正在处理Maybe,就该库而言,捕获所需语义的类型是Option (Max a) - 一个与{{1}具有相同二进制运算的monoid半群,并使用Max作为标识元素。那么函数就变成了:

Nothing

...因为它只是maxOpt :: Ord a => Option (Max a) -> Option (Max a) -> Option (Max a) maxOpt a b = a <> b 的{​​{1}}运算符不值得写。您还可以获得适用于<>Option (Max a)的所有其他实用程序函数和类,例如,要查找Semigroup的最大元素,您只需使用{{3} }}

scalaz库附带了the mconcat functionSemigroup特征,以及实现这些特征的Monoid,所以事实上我在这里展示的内容Haskell也存在于scalaz中。