【问题标题】:Type mismatch with map and flatMap类型与 map 和 flatMap 不匹配
【发布时间】:2013-01-29 08:38:53
【问题描述】:

在尝试使用 scala 中的选项时,我遇到了这个特殊的问题。

我开始创建一个 List[Option[Int]],如下所示:

scala> List(Some(1),Some(2),None,Some(3))
res0: List[Option[Int]] = List(Some(1), Some(2), None, Some(3))

然后我尝试在 res0 中的列表条目上映射一个加法 1,如下所示:

scala> res0 map (_ + 1)

这给了我错误:

<console>:9: error: type mismatch;
 found   : Int(1)
 required: String
              res0 map (_ + 1)
                            ^

然后我尝试对条目进行平面映射,如下所示:

scala> res0 flatMap (_ + 1)

这给了我同样的错误:

<console>:9: error: type mismatch;
 found   : Int(1)
 required: String
              res0 flatMap (_ + 1)
                                ^

但是像res0.flatMap(r =&gt; r) 这样的东西可以正常工作,结果是:

res9: List[Int] = List(1, 2, 3)

谁能告诉我为什么将条目添加到 1 会导致 map 和 flatMap 都失败?

【问题讨论】:

    标签: scala types map


    【解决方案1】:

    您尝试的前两件事失败了,因为您试图将Option 添加到Int,这是不可能的。

    出现奇怪的错误消息是因为 Scala 假设,因为 Option 没有 + 方法,您正在尝试 String 连接,但您必须将 Option 添加到 @ 987654327@,或将String 转换为Option,而您都没有这样做,因此出现错误消息。

    在最后一种情况下,您没有尝试添加任何内容,您只是按原样返回Option,因此没有错误消息。

    【讨论】:

    • 我认为 sc_ray 也感到困惑,因为在尝试 res0 flatMap (_ + 1) 时,他可能认为它等同于 res0.flatten.map(_ + 1)(这显然是他试图做的),而实际上它等同于 @ 987654333@。也许flatMap 应该被称为mapFlat 而不是flatMap:-)
    【解决方案2】:

    要增加所有不是None 的值,您还需要映射列表的每个Option 元素,如下所示:

    scala> res0.map(_.map(_ + 1))
    res1: List[Option[Int]] = List(Some(2), Some(3), None, Some(4))
    

    如果你想过滤掉Nones,你确实会使用flatMap

    scala> res0.flatMap(_.map(_ + 1))
    res2: List[Int] = List(2, 3, 4)
    

    【讨论】:

      【解决方案3】:

      赋予flatMap 的函数以及赋予map 的函数都采用列表元素类型的值——在本例中为Option[Int]。但是您的函数_ + 1 需要Int,而不是Option[Int],因此在这种情况下,您不能将它用作mapflatMap 的参数。此外,给flatMap 的函数应该返回一个可迭代的¹,但你的函数会返回一个数字。

      这将满足您的需求:res0 flatMap (_ map (_ + 1))。这里给flatMap 的函数接受Option[Int] 并通过在选项上调用map 返回Option[Int]flatMap 然后接受函数返回的选项并将它们连接起来。

      ¹ 技术上是GenTraversableOnce

      【讨论】:

        【解决方案4】:

        您尝试在List[Option[Int]] 中的每个元素e 上调用e.+(1),但+ 不是Option[_] 声明的函数。然而,字符串连接是可能的(我假设存在从AnyString 的隐式),但前提是第二个参数也是一个字符串(不确定为什么不考虑我假设的隐式存在在这里)。

        您可以通过使用@korefn 建议的默认值来解决这个问题,或者在map 的另一个调用中“隐藏”Some(x)None 之间的区别,即通过

        map(_.map(_ + 1))
        

        【讨论】:

          【解决方案5】:

          它们失败是因为类型错误并且编译器正确地声明了它。

          map 案例失败,因为map 需要一个函数A =&gt; B。在您的代码中,A =&gt; B 实际上是 Int =&gt; Int,这将不起作用,因为在您的列表中调用 map 意味着 A 实际上是 Option[Int]

          此外,flatMap 需要 A =&gt; F[B] 形式的函数。因此,如果您执行res0 flatMap { o =&gt; o map { a =&gt; a + 1 } },您将得到答案。这基本上是:

          for {
            element <- res0 // o above
            value <- element // a above
          } yield value + 1
          

          【讨论】:

            【解决方案6】:

            尝试使用 get 从Some[Int] 提取值到Int 允许计算value + 1 即:

            res0 map{_.getOrElse(0) + 1}
            

            正如@Sepp2k 所指出的,您也可以使用collect 来避免None 的默认值

            res0 collect {case Some(x) => x + 1 }
            

            【讨论】:

            • 我认为 OP 不想将 Nones 视为 0。至少他没有说任何暗示。
            • 这是为了避免在 get 上调用 get 时出现 NoSuchElementException None 一个详细的替代方法是使用 collect 来避免这种情况。
            • 如果两个事物产生不同的结果,它们就不能相互替代。
            • 在这种情况下,它将有助于将 Some(x) 解压缩为 x,而且它看起来不错(我喜欢这种语法)。
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2015-06-04
            • 2019-01-02
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2010-09-20
            • 1970-01-01
            相关资源
            最近更新 更多