Scala Seq包含对空序列非法或未定义的方法,例如max
为了避免这种情况,我通常通过将序列包装在一个选项中并将空映射为None来解决这个问题。可以这样做:
def wrap[T](s: Seq[T]): Option[Seq[T]] =
s.headOption map { _ => s }
也可以通过模式匹配或if-else来解决。
这些方法都不容易流畅地使用,坦率地说,看起来有点尴尬。
我希望能够做这样的事情:
someStore
.giveMeASequence
.wrapInOption map { nonEmpty:Seq[T] =>
nonEmpty.max
} map (_ + 42)
我很犹豫是否将操作包装在Try单子中,因为可以预测和避免异常。
我处理问题的方式不对吗?
如果没有,有什么库方法可以让我不那么尴尬吗?
如果你只是想做转换,为什么不使用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
以同样的方式,您可以添加一个方法来在Option中包装一个序列:
implicit def seqWithOptionWrapping[A](s:Seq[A]) =
new { def wrapInOption = if (s.isEmpty) None else Some(s) }
正如我在评论中提到的,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
.
您可以通过隐式类使用安全方法扩展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)