【问题标题】:Map versus FlatMap on String字符串上的 Map 与 FlatMap
【发布时间】:2013-10-06 13:42:19
【问题描述】:

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

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

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

但是,我可以为List[String] 执行map 而不是flatmap

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] 是在字符串上调用map 的返回类型?

【问题讨论】:

  • 因为你不能把两个字符放在一个Char类型中?而字符+字符是产生String类型的两个字符。

标签: scala map


【解决方案1】:

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

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

【讨论】:

  • 如果不查看实现或尝试string map 将其转换为IndexSeq[String] 并且flatten 将其转换回String,我应该如何猜测。在我看来,每次我将 mapflatMap 应用于 scala 中的某些数据结构时,我都需要尝试一下或查看它的实现以了解它的行为,是这样吗?如果是这样,这是一个好的设计(清晰的代码)吗?
【解决方案2】:

你还有一个有趣的“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])。
flatMapmap 和@ 的组合987654332@,所以它首先在序列上运行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)

【讨论】:

  • 嗨@VonC 我正在尝试您共享的链接中的示例。对于他的博客上显示的以下示例,他能够获得以下示例的结果,但是当我尝试运行它时,当我尝试应用 toInt 函数时,我得到字符串“foo”的数字格式异常。 scala> val strings = Seq("1", "2", "foo", "3", "bar")strings: 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)
  • @Novice 有趣。最好将其单独作为一个问题供其他人测试。
  • Because flatMap treats a String as a sequence of Char 我应该怎么猜?通过尝试?通过查看它的实现?
  • @Jas 我同意。 safaribooksonline.com/library/view/learning-scala/9781449368814/… 给出了一条线索,描述了 Sequence 类型,并在其中列出了 String(!):“在 Scala 中,String 和其他集合一样是一个有效的集合。毕竟,“字符串”的名称来源于“
【解决方案3】:

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

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

【讨论】:

    【解决方案4】:

    使用map,您将获取一个字符列表并将其转换为字符串列表。这就是你看到的结果。 map 永远不会改变列表的长度——字符串列表中的元素与原始字符串中的字符一样多。

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

    【讨论】:

      【解决方案5】:

      在您运行 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
      

      【讨论】:

        猜你喜欢
        • 2010-11-06
        • 2018-05-07
        • 1970-01-01
        • 2023-03-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多