2016-12-16 74 views
0

我是Scala/Spark世界的新手。我试图找出为什么这个代码是可以接受的:scala中的flatMap函数和返回类型错误

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 
    } 
    } 
} 

我知道它与类型不匹配的事,但也差不多了。我的问题是为什么我不能返回一个元组?

回答

2

要看看发生了什么事情,让我们的类型明确:

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

没有什么底型,以及任何你试图用Nothing succeds合并。因此,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 - 但这对你没有帮助。所以这就是为什么你会得到错误信息,因为一个元组=选项

1

这与Spark没有多大关系,而是一般的Scala或函数式编程。 flatMap操作使底层结构变得平坦。例如,List[List[A]]List[A]Option[Option[A]]Option[A]。同样,RDD[List[A]]RDD[A],它将跳过RDD[None]的行。

在这里使用平面地图不是很直观,你也可以先做一个地图,然后做一个过滤操作。通过使用两种操作,这看起来更加昂贵,但由于它们是一次性的,因此Spark将优化操作下的操作,特别是使用新的Tungsten engine

2

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 }