字符串上的地图与FlatMap

时间:2013-10-06 13:42:19

标签: scala map

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的返回类型?

5 个答案:

答案 0 :(得分:28)

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

然后

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

答案 1 :(得分:13)

您还有一个有趣的“collection of Scala flatMap examples”,其中第一个说明了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,显示结果。

     

您可以通过运行地图然后展平自己来看到这一点:

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