Scala中的flatmap功能和返回类型错误



我是Scala/Spark World的新手。我试图弄清楚为什么可以接受此代码:

val artistID = rawArtistData.flatMap { line =>
  val (id, name) = line.span(_ != 't')
  if (name.isEmpty) {
    None
  } else {
    try {
      Some(id.toInt, name.trim)
    } catch {
      case e: NumberFormatException => None
    }
  }
}

这不是:

val artistID = rawArtistData.flatMap { line =>
  val (id, name) = line.span(_ != 't')
  if (name.isEmpty) {
    None
  } else {
    try {
      (id.toInt, name.trim)
    } catch {
      case e: NumberFormatException => None
    }
  }
}

我知道这与类型不匹配有关,但仅此而已。我的问题是为什么我不能退还元组?

要查看正在发生的事情,让我们清晰地说:

  if (name.isEmpty) {
    None // Option[Nothing]
  } else {
    try {
      Some(id.toInt, name.trim) // Option[(Int, String)]
    } catch {
      case e: NumberFormatException => None // Option[Nothing]
    }
  }

没有什么是底部类型,而您尝试与Nothing成功合并的任何东西。因此,NoneSome(...)之间的统一总是有效的 - 使用List(None, Some(...))尝试一下,这将始终导致List[Option[...]]类型。

现在,如果您在那里有元组

    try {
      Some(id.toInt, name.trim) // (Int, String)
    } catch {
      case e: NumberFormatException => None // Option[Nothing]
    }

编译器将尝试统一(Int, String)Option[Nothing] ...不能。好吧,这有点可以,因为Scala中任何两件事的超模型始终是Any-但这对您无济于事。这就是为什么您会收到错误消息的原因,因为元组!=一个选项

flatMap方法需要一个功能f,具有以下签名:

(T) ⇒ TraversableOnce[U]

(_, _)Option[(_, _)]都不是TraversableOnce,但是后者的伴随对象具有隐式option2Iterable方法:

def option2Iterable[A](xo: Option[A]): Iterable[A]

Iterable[_]TraversableOnce[_]。这就是为什么您可以返回Option并且无法返回普通Tuple2[_, _]

实际上,您可以用Try简化此内容:

import scala.util.Try
rawArtistData.flatMap { line => Try {
  line.span(_ != 't') match {
    case (id, name) => (id.toInt, name)
  }
}.toOption }

这与Spark无关,而与Scala或功能编程有关。Flatmap操作使基础结构变平。例如,List[List[A]]List[A],或Option[Option[A]]Option[A]。同样,RDD[List[A]]RDD[A],它将跳过RDD[None]的行。

在此处使用Flatmap并不是真正的直观,您也可以首先进行地图,然后进行过滤器操作。通过使用两个操作,这看起来可能更昂贵,但是由于它们是单一的,Spark会优化引擎盖下的操作,尤其是使用新的钨引擎。

相关内容