【问题标题】:Any way of simplifying this Scala code (pattern matching)?有什么方法可以简化这个 Scala 代码(模式匹配)?
【发布时间】:2018-04-06 17:46:29
【问题描述】:

我有一些用户提供的值作为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

【问题讨论】:

  • object PositiveInt { def unapply(repr: String): Option[Int] = Try(repr.toInt).filter(_ &gt;= 0)top match { case Some(PositiveInt(x)) =&gt; ???; case _ =&gt; sys.error(s"Invalid int: $repr") }

标签: scala functional-programming pattern-matching


【解决方案1】:

是的,可以通过使用flatMapfor-comprehensionscollect 来简化很多:

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
}

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

然后我们将两者都放入一个 List 并仅收集非空值,从而生成 ErrList ,而无需创建中间 Buffer

使用猫库中的 Validated 类型可以找到执行此类操作的更简单方法:https://typelevel.org/cats/datatypes/validated.html

【讨论】:

  • 守卫不是生成器的一部分(即i &lt;- 是错误的)。您可以按名称调用 a 参数,以免对其进行不必要的评估。你不需要collect,只需要flattenList
【解决方案2】:

使用for-comprehensions 的另一种方法是使用mapfilter

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

map 调用将产生一个Option[Option[Int]],然后根据嵌套的Option 的值进行过滤(如果选项为None,则默认为-1)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-02-04
    • 1970-01-01
    • 2015-08-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多