听Functional Programming Principles in Scala的收藏讲座,我看到了这个例子:
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
然后,我很好奇为什么奥德斯基先生在这里没有使用map
。但是,当我尝试使用地图时,我得到的结果与我预期的不同。
scala> s.map(c => ("." + c))
res8: scala.collection.immutable.IndexedSeq[String] = Vector(.H, .e, .l, .l, .o,
". ", .W, .o, .r, .l,
我期望上面的调用返回一个String,因为我是map
,即将函数应用于“序列”中的每个项目,然后返回一个新的“序列”。
但是,我可以为map
执行flatmap
而不是List[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)
为什么IndexedSeq[String]
在String上调用map
的返回类型?
答案 0 :(得分:28)
此行为的原因是,为了将“map”应用于String,Scala将字符串视为字符序列(IndexedSeq[String]
)。这是您通过映射调用获得的结果,对于所述序列的每个元素,应用该操作。由于Scala将字符串视为应用map
的序列,因此map
返回。
flatMap
然后在该序列上调用flatten
,然后将其“转换”回字符串
答案 1 :(得分:13)
您还有一个有趣的“collection of Scala flatMap examples”,其中第一个说明了flatMap
和map
之间的差异:
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)
相当不同,对吧? 由于
flatMap
将String
视为Char
的序列,因此会将生成的字符串列表展平为一系列字符(Seq[Char]
)。
flatMap
是map
和flatten
的组合,因此首先在序列上运行map
,然后运行flatten
,显示结果。您可以通过运行地图然后展平自己来看到这一点:
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)
答案 2 :(得分:7)
你的map函数c => ("." + c)
接受一个char并返回一个String。这就像拿一个List并返回一个列表列表。 flatMap将其展平。
如果要返回char而不是String,则不需要将结果展平,例如"abc".map(c => (c + 1).toChar)
返回“bcd”。
答案 3 :(得分:1)
使用map
,您将获取一个字符列表并将其转换为字符串列表。这就是你看到的结果。 map
永远不会更改列表的长度 - 字符串列表包含与原始字符串一样多的元素。
使用flatMap
,您将获取一个字符列表并将其转换为字符串列表,然后将再次放入单个字符串中。当您想要将列表中的一个元素转换为多个元素而不创建列表列表时,flatMap
非常有用。 (这当然也意味着结果列表可以有任何长度,包括0 - 除非你从空列表开始,否则map
不可能这样做。)
答案 4 :(得分:0)
在运行 map ,然后运行 flattern 的情况下使用flatMap。具体情况如下:
•您正在使用 map (或 for / 表达式)从现有集合中创建新集合。
•生成的集合是列表列表。
•在 map 之后立即调用展平(或 for / 表达式)。
当你处于这种情况时,你可以使用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
使用地图方法(需要3个步骤)
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