有什么方法可以简化这个Scala代码(模式匹配)吗?

时间:2017-10-25 14:58:39

标签: scala functional-programming pattern-matching

我有一些用户提供的值Option[String]。我想仅在它们非空时验证它们。

验证只是检查字符串是否可以转换为int,并且不小于0.

有没有办法简化这段代码,或者让它更具可读性?

val top: Option[String] = ...
val skip: Option[String] = ...

val validationErrors = new ListBuffer[Err]()

top match {
  case Some(x) => if (x.toIntOpt.isEmpty || x.toIntOpt.get < 0) validationErrors += PositiveIntegerRequired("$top")
}
skip match {
  case Some(x) => if (x.toIntOpt.isEmpty || x.toIntOpt.get < 0) validationErrors += PositiveIntegerRequired("$skip")
}

这是toIntOpt助手:

def toIntOpt: Option[Int] = Try(s.toInt).toOption

2 个答案:

答案 0 :(得分:3)

是的,通过使用flatMapfor-comprehensions以及collect可以简化它:

def checkInt[A](stringOpt: Option[String], a: A): Option[A] = for {
  s <- stringOpt
  i <- s.toIntOpt if (i < 0)
} yield a

val validationErrors = List(
  checkInt(top, PositiveIntegerRequired("$top")),
  checkInt(skip, PositiveIntegerRequired("$skip"))
).collect {
  case Some(x) => x
}

如果原始checkInt非空且包含无效的负整数,则第一个函数a会返回值Option

然后我们将两者放入List并仅收集非空的值,从而产生List Err,而无需创建中间Buffer

使用cat库中的Validated类型可以找到更简单的方法:https://typelevel.org/cats/datatypes/validated.html

答案 1 :(得分:0)

使用for-comprehension的替代方法是将mapfilter一起使用。

top.map(toIntOpt)
   .filter(i => i.getOrElse(-1) < 0)
   .foreach(_ => validationErrors += PositiveIntegerRequired("$top"))

map调用将生成Option[Option[Int]],然后根据嵌套Option的值对其进行过滤(如果选项为{{1,则默认为-1 }})。