总结Scala中的选项列表

时间:2016-09-05 20:16:56

标签: list scala sum

如何使用以下规则汇总选项列表List[Option[Double]]

  • List(Some(1), ..., Some(n)) --> Some(1 + ... + n)
  • List(Some(1), ..., Some(n), None) --> None
  • List(None, ..., None) --> None

4 个答案:

答案 0 :(得分:4)

这是一个直接的foldMap方法调用,如果你正在使用Cats,你应该只使用它,因为它会折叠一个使用Monoid对值进行求和的集合。

否则,为了避免使用forall遍历整个列表来检查是否定义了整个选项集,您可以使用foldLeft,并使用可以产生第一次在链中找到空元素时None

def sumList[ T ] (list: List[Option[T]])(implicit ev: Numeric[T]): Option[T] = {
  list.foldLeft(Option(ev.zero)) { case (acc, el) => 
    el.flatMap(value => acc.map(ac => ev.plus(ac, value)))
  }
}

sumList(List(None, None, Some(5)))
res10: Option[Int] = None

scala> sumList(List(None, None, Some(5F)))
res11: Option[Float] = None

scala> sumList[Double](List(None, None, None))
res13: Option[Double] = None

scala> sumList(List(Some(5), Some(15)))
res14: Option[Int] = Some(20)

为避免return您只需使用递归(更新,上面不需要返回,但可能更容易理解):

@annotation.tailrec
def sumListRec[T](list: List[Option[T]], acc: T)(implicit ev: Numeric[T]): Option[T] = {
  list match {
    // if the list still has elements
    case head :: tail => head match {
      // add the value to the accumulator and keep going
      case Some(value) => sumListRec(tail, ev.plus(acc, value))
      // if you found a None, disregard whatever sum we built so far
      // and just return None
      case None => None
    }
    // If the list is empty, it means we've successfully reached
    // the end of the list, so we just return the sum we built.
    case Nil => Some(acc)
  }
}

观看它的实际操作:

scala> sumListRec(List(Some(5D), Some(5D)), 0D)
res5: Option[Double] = Some(10.0)

sumListRec(List(None, None, Some(5D)), 0D)
res2: Option[Double] = None

scala> sumListRec(List(None, None), 0D)
res6: Option[Double] = None

答案 1 :(得分:2)

如果您使用Scalaz,这非常简单:

{{1}}

答案 2 :(得分:1)

使用折叠甚至有一种更简单的方法:

val ls = List(Some(2.1d), Some(5.6d), Some(4.3d), Some(1.2))

ls.fold(Option(0d))((rs,x) => for(n <- x; m <- rs) yield {n+m})
  

=&GT;一些(13.2)

val ls = List(Some(2.1d),None, Some(5.6d), Some(4.3d), Some(1.2))

ls.fold(Option(0d))((rs,x) => for(n <- x; m <- rs) yield {n+m})
  

=&GT;无

答案 3 :(得分:0)

你可以这样做。 修改:将代码更改为在遇到None后立即返回。我承认,这种优化既不会使代码复杂化也不会妨碍可读性(正如我之前所想的那样)。

def sumOpt(list: List[Option[Double]]): Option[Double] = list.reduce((x,y) => {
  if (x.isEmpty || y.isEmpty) return None
  else Some(x.get + y.get)
})
sumOpt(list)