27

Scala 中的函数式编程原理的 Collections 讲座,我看到了这个例子:

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这里使用 a。但是,当我尝试使用 map 时,得到的结果与我预期的不同。

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

我希望上面的调用返回一个字符串,因为我正在map执行,即对“序列”中的每个项目应用一个函数,然后返回一个新的“序列”。

但是,我可以执行 amap而不是flatmapfor a 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]调用mapString的返回类型是a?

4

5 回答 5

31

这种行为的原因是,为了将“映射”应用于字符串,Scala 将字符串视为字符序列 ( IndexedSeq[String])。这是您通过调用 map 得到的结果,其中对于所述序列的每个元素,都应用了操作。由于 Scala 将字符串视为要应用的序列map,这就是map返回的内容。

flatMap然后简单地调用flatten该序列,然后将其“转换”回字符串

于 2013-10-06T13:46:27.717 回答
16

您还有一个有趣的“Scala flatMap 示例集合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将 aString视为 的序列Char,所以它将生成的字符串列表扁平化为字符序列 ( Seq[Char])。
flatMap是 和 的组合mapflatten因此它首先map在序列上运行,然后运行flatten​​,给出显示的结果。

您可以通过运行 map 来查看这一点,然后将自己展平:

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)
于 2016-03-19T22:54:12.653 回答
7

您的地图函数c => ("." + c)接受一个字符并返回一个字符串。这就像获取一个列表并返回一个列表列表。flatMap 将其变平。

如果您要返回一个字符而不是字符串,则不需要将结果展平,例如"abc".map(c => (c + 1).toChar)返回“bcd”。

于 2013-10-06T13:52:46.327 回答
1

map您一起获取字符列表并将其转换为字符串列表。这就是你看到的结果。Amap从不改变列表的长度——字符串列表的元素与原始字符串的字符数一样多。

flatMap您正在获取一个字符列表并将其转换为字符串列表,然后您将这些字符串再次组合成一个字符串flatMap当您想将列表中的一个元素转换为多个元素而不创建列表列表时很有用。map(这当然也意味着结果列表可以有任何长度,包括 0——除非你从空列表开始,否则这是不可能的。)

于 2013-10-06T13:53:34.730 回答
0

在运行map后跟flattern的情况下使用 flatMap 。具体情况是这样的:

• 您正在使用map(或for/yield表达式)从现有集合创建新集合。

• 生成的集合是一个列表列表。

• 在map(或for/yield表达式)之后立即调用flatten 。

在这种情况下,您可以改用 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

使用 map 方法(需要 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
于 2017-07-25T05:37:40.963 回答