将 Seq[T] 转换为 Option[Seq[T]]

Converting Seq[T] into Option[Seq[T]]

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

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

def wrap[T](s: Seq[T]): Option[Seq[T]] =
    s.headOption map { _ => s }

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

None 的方法很容易以流畅的方式使用,坦率地说似乎有点笨拙。

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

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

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

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

如果没有,是否有任何库方法可供我使用以减少尴尬?

如果您只想进行转换,为什么不使用 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) }

您可以通过隐式 class 使用您的安全方法扩展 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)   

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

import scalaz._
import Scalaz._

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

println(result)

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