如果没有任何内容,请使用默认值替换Scala中的选项

时间:2015-07-23 18:33:39

标签: scala

假设我有(Option[Long], Option[Long], Option[Long], Option[Long], Option[Long])类型的项目,并且我想将其转换为(Long, Long, Long, Long, Long)类型的项目。我希望每个坐标都包含选项的值(如果选项包含"某些"值),否则为零。

通常如果我有Option[Long]类型的项目,我会做类似的事情

item match {
    case Some(n) => n
    case None => 0
}

但我不能用5坐标项来做,除非我想列出所有32种可能性。我该怎么做呢?

4 个答案:

答案 0 :(得分:4)

简单的解决方案:

item match {
  case (a, b, c, d, e) => (a.getOrElse(0), b.getOrElse(0), c.getOrElse(0), d.getOrElse(0), e.getOrElse(0))
}

显然这不是非常通用的。为此,你可能想看看Shapeless,但我会把答案留给常驻专家。 ;)

答案 1 :(得分:2)

使用Shapeless你可以做到:

import shapeless._
import syntax.std.tuple._
import poly._

object defaultValue extends Poly1 {
  implicit def defaultOptionLong = at[Option[Long]](_.getOrElse(0L))
}

val tuple : (Option[Long], Option[Long], Option[Long], Option[Long], Option[Long]) = 
  (Some(1L), None, Some(3L), Some(4L), None)

tuple.map(defaultValue)
// (Long, Long, Long, Long, Long) = (1,0,3,4,0)

如果您不使用Option[Int],则需要明确指定类型Option.apply。(请参阅此question)。

(Option(1L), Option(2L)).map(defaultValue) 
// (Long, Long) = (1,2)

(Some(3L), Some(4L)).map(defaulValue) // does not compile
val t : (Option[Long], Option[Long]) = (Some(3L), Some(4L))
t.map(defaultValue) 
// (Long, Long) = (3,4)

(Option(5), None).map(defaultValue) // does not compile
val t2 (Option[Long], Option[Long]) = (Option(5), None)
t2.map(defaultValue) 
// (Long, Long) = (5,0)

我们还可以为其他类型提供默认值:

object defaultValue extends Poly1 {
  implicit def caseLong = at[Option[Long]](_.getOrElse(0L))
  implicit def caseInt = at[Option[Int]](_.getOrElse(0))
  implicit def caseString = at[Option[String]](_.getOrElse("scala"))
}

val tuple2 : (Option[Int], Option[Long], Option[String]) = (None, None, None)
tuple2.map(defaultValue)
// (Int, Long, String) = (0,0,scala)

修改:需要明确声明Some(5L)Option[Long]的问题可以使用poly函数中的泛型来解决:

objec defaultValue extends Poly1 {
  implicit def caseLong[L <: Option[Long]] = at[L](_.getOrElse(0L))
  implicit def caseInt[I <: Option[Int]] = at[I](_.getOrElse(0))
  implicit def caseString[S <: Option[String]] = at[S](_.getOrElse("scala"))
}

(Some("A"), Some(1), None: Option[Int], None: Option[String]).map(defaultValue)
// (String, Int, Int, String) = (A,1,0,scala)

答案 2 :(得分:1)

您可以这样做:

val res = for {
   a <- item._1.orElse(0L)
   b <- item._2.orElse(0L)
   c <- item._3.orElse(0L)
   d <- item._4.orElse(0L)
   e <- item._5.orElse(0L)
} yield (a, b, c, d, e)

不是最好的,但易于实施和理解。

答案 3 :(得分:1)

另一种可能的解决方案:

item.productIterator.collect{
   case Some(a: Int) => a
   case _            => 0
}.toList match {
    case List(a,b,c,d,e) => (a,b,c,d,e)
    case _ => (0,0,0,0,0) //or throw exception depending on your logic
}