将Seq [T]转换为选项[Seq [T]]

时间:2015-08-07 11:58:54

标签: scala functional-programming monads seq

Scala Seq包含对于空序列非法或未定义的方法,例如max

为了保护自己,我经常通过将序列包装在一个选项中并将空映射到None来解决问题。可以这样做:

someStore
   .giveMeASequence
   .wrapInOption map { nonEmpty:Seq[T] =>
      nonEmpty.max
    } map (_ + 42)

它也可以通过模式匹配或if-else来解决。

这些方法都不易于流畅地使用,坦率地说看起来有点尴尬。

我希望能够做到这样的事情:

library(ggplot2)

dt = data.frame(Chr = c("c1","c1","c1","c2","c2","c2","c3","c3","c3"),
                x = c(1,2,3,4,5,6,7,8,9),
                y = c(2,4,5,2,3,4,6,6,7))

ggplot(dt, aes(x,y, col=Chr)) +
  geom_point(size = 3) +
  geom_line() +
  facet_grid(. ~ Chr) # remove this to have all lines in same plot

我对在Try monad中包装操作犹豫不决,因为可以预测和避免异常。

我是以错误的方式解决问题吗?

如果没有,是否有任何图书馆方法可以让它不那么尴尬?

3 个答案:

答案 0 :(得分:3)

如果您只是想进行转换,为什么不使用if / else?

def wrap[A](s: Seq[A]) = if (s.isEmpty) None else Some(s)

或者,您可以定义自己的maxOpt方法:

implicit def seqWithMaxOpt[A:Ordering](s:Seq[A]) =
  new { def maxOpt = Try(s.max).toOption }

并称之为

val s:Seq[Int] = Seq()
s.maxOpt

以同样的方式,您可以添加一个方法来包装选项中的序列:

   implicit def seqWithOptionWrapping[A](s:Seq[A]) =
  new { def wrapInOption = if (s.isEmpty) None else Some(s) }

答案 1 :(得分:3)

您可以通过隐式类使用安全方法扩展Seq

 implicit class SafeSeq[T](s: Seq[T]) {
    def safeMax(implicit cmp: Ordering[T]): Option[T] =
      if (s.isEmpty) None
      else Some(s.max)

    //other safe operations you want
  }

  println(Seq.empty[Int].safeMax.map(_ + 42)) // None
  println(Seq(1).safeMax.map(_ + 42))         // Some(43)   

答案 2 :(得分:3)

正如我在评论中提到的,scalaz NonEmptyList课程对您有所帮助。通过导入scalaz pimping,您可以访问toNel上可以调用的List方法。如果此列表确实为空,则无法继续映射它。如果它不是空的,你的确可以映射它。请考虑以下示例:

import scalaz._
import Scalaz._

val nonEmpty = List(1,2,3)
val result = 
  nonEmpty.
    toNel.
    map(_.list.max).
    map(_ + 42)

println(result)

如果您运行此代码,它将打印Some(45)。如果该列表为空,则会产生None