2013-10-06 73 views
15

聆聽講座集合從Functional Programming Principles in Scala,我看到這個例子:地圖與FlatMap上串

scala> val s = "Hello World" 

scala> s.flatMap(c => ("." + c)) // prepend each element with a period 
res5: String = .H.e.l.l.o. .W.o.r.l.d 

然後,我很好奇,爲什麼Odersky的先生沒有使用map這裏。但是,當我嘗試地圖時,我得到了與我預期不同的結果。

scala> s.map(c => ("." + c)) 
res8: scala.collection.immutable.IndexedSeq[String] = Vector(.H, .e, .l, .l, .o, 
                  ". ", .W, .o, .r, .l, 

我預計,上述調用返回一個字符串,因爲我map -ing,即應用功能,在每個項目「序列」,然後返回一個新的「順序」。

不過,我可以執行map而不是flatmapList[String]

scala> val sList = s.toList 
sList: List[Char] = List(H, e, l, l, o, , W, o, r, l, d) 

scala> sList.map(c => "." + c) 
res9: List[String] = List(.H, .e, .l, .l, .o, ". ", .W, .o, .r, .l, .d) 

爲什麼呼籲字符串mapIndexedSeq[String]返回類型?

+2

(需要3步),因爲你不能將兩個字符放在單個字符類型中?而字符+字符是產生字符串類型的兩個字符。 –

回答

23

此行爲的原因是,爲了將「映射」應用於字符串,Scala將字符串視爲字符序列(IndexedSeq[String])。這是地圖調用的結果,其中對於所述序列的每個元素,都應用該操作。由於Scala將字符串視爲應用map的序列,這就是map返回的結果。

flatMap然後簡單地調用該序列flatten之後,然後「轉換」回一個String

+1

我該如何猜測,而不是在執行或試圖出來,'字符串'映射將其轉換爲'IndexSeq [String]',並且'flatten'將它轉換回'String'。這聽起來對我來說每次我將'map'或'flatMap'應用於scala中的某個數據結構時,我都需要嘗試一下,或者看看它的實現以瞭解它的行爲,是這樣嗎?如果是的話,這是一個很好的設計(清晰的代碼明智)? – Jas

7

你的地圖功能c => ("." + c)需要一個char並返回一個字符串。這就像拿一個List並返回List列表。 flatMap縮小了背部。

如果你要返回一個字符而不是一個字符串,你將不需要結果變平,例如, "abc".map(c => (c + 1).toChar)返回「bcd」。

1

With map您正在查看字符列表並將其轉換爲字符串列表。這就是你看到的結果。 A map從不改變列表的長度 - 字符串列表包含與原始字符串包含字符一樣多的元素。

隨着flatMap你正在接受一個字符列表,並把它變成一個字符串列表和然後你把這些字符串再次混合成一個單一的字符串。當您想要將列表中的一個元素轉換爲多個元素而不創建列表列表時,flatMap非常有用。 (這當然也意味着結果列表可以有任意長度,包括0 - 除非你從空列表開始,否則這是不可能的。)

7

你也有一個有趣的「01​​」,這是第一個有趣的「01​​」其示出flatMapmap之間的差:

scala> val fruits = Seq("apple", "banana", "orange") 
fruits: Seq[java.lang.String] = List(apple, banana, orange) 

scala> fruits.map(_.toUpperCase) 
res0: Seq[java.lang.String] = List(APPLE, BANANA, ORANGE) 

scala> fruits.flatMap(_.toUpperCase) 
res1: Seq[Char] = List(A, P, P, L, E, B, A, N, A, N, A, O, R, A, N, G, E) 

相當差,是嗎?
由於flatMapString視爲Char的一個序列,它將結果列表中的字符串變爲字符序列(Seq[Char])。
flatMapmapflatten的組合,所以它首先在序列上運行map,然後運行flatten,給出顯示的結果。在您運行地圖其次flattern情況

scala> val mapResult = fruits.map(_.toUpperCase) 
mapResult: Seq[String] = List(APPLE, BANANA, ORANGE) 

scala> val flattenResult = mapResult.flatten 
flattenResult: Seq[Char] = List(A, P, P, L, E, B, A, N, A, N, A, O, R, A, N, G, E) 
+0

嗨@VonC我正在嘗試他們在你分享的鏈接中的例子。對於他的博客中顯示的以下示例,他可以得到下面的示例的結果,但是當我嘗試運行它時,我正在嘗試應用'toInt'函數時得到字符串「foo」的數字格式異常。字符串:Seq [java.lang.String] = List(1,2,foo) ,3,bar)' 'scala> strings.map(toInt)' 'res0:Seq [Option [Int]] = List(Some(1),Some(2),None,Some(3),None )' 'scala> strings.flatMap(toInt)' 'res1:Seq [Int] = List(1,2,3)' – Explorer

+0

@Novice有趣。對於其他人來說,這將是最好的問題。 – VonC

+0

'因爲flatMap將字符串視爲Char序列,我該如何猜測?通過嘗試它?通過看它的實現? – Jas

0

使用flatMap:

可以通過運行圖看到這一點,然後壓平自己。具體情況是這樣的:

•您使用地圖(或用於/產量表達式)從現有的集合創建一個新的集合。

•生成的集合是一個List列表。

•您撥打壓扁後,立即地圖(或用於/產量表達)。

當您處於這種情況時,您可以改爲使用flatMap。

實施例:從袋中

val bag = List("1", "2", "three", "4", "one hundred seventy five") 

def toInt(in: String): Option[Int] = { 
try { 
Some(Integer.parseInt(in.trim)) 
} catch { 
case e: Exception => None 
} 
} 

添加所有整數使用flatMap方法

> bag.flatMap(toInt).sum 

使用地圖方法

bag.map(toInt) // List[Option[Int]] = List(Some(1), Some(2), None, Some(4), None) 

bag.map(toInt).flatten //List[Int] = List(1, 2, 4) 

bag.map(toInt).flatten.sum //Int = 7 
相關問題