2016-12-16 75 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 }