【问题标题】:Why does this scala fold return Equals types?为什么这个 scala 折叠返回 Equals 类型?
【发布时间】:2015-02-09 19:54:36
【问题描述】:

我正在尝试在 scala 中返回地图。这是我的代码:

    val interestRegEx = """(\w+) Interests \((.+ Interest)\)""".r
    val singleAttributes = Seq("Sport Interests (Some Interest):Running,Swimming","Something else:True")
    val interests = singleAttributes.map { x =>
      // e.g. ("Sport Interests (Some Interest)", "Running,Swimming") should
      // result in ("Sport Running" -> "Some Interest", "Sport Swimming" -> "Some Interest")
      val attVal = x.split(':')

      attVal(0) match {
        case interestRegEx(interestProduct, interestAmount) =>
          Some(attVal(1).split(",").map { i =>
            Map(s"$interestProduct $i" -> interestAmount)
          }.reduce(_ ++ _))
        case _ => None
      }
    }.fold(Map[String, String]())(_) //.reduce(_ + _)

问题是试图将集合减少为单个 Map[String, String]。我认为折叠可能会起作用,但因为它可能不需要在之后添加reduce(_ + _),但这也不起作用。

我不明白的部分是 IntelliJ 告诉我 interests 的类型为 ((Equals, Equals) => Equals) => Equals。怎么回事?这些Equals 来自哪里,为什么不只是将所有 Map 加在一起以返回包含所有键和值的 Map?

【问题讨论】:

    标签: scala


    【解决方案1】:

    如果我们简化您的示例,我们将得到:

      val interests = Seq[String]().map { x =>
        Option[Map[String, String]](Map("k" -> "v"))
      }.fold(Map[String, String]())(_)
    

    这意味着我们正在尝试fold Seq[Option[Map[String, String]]] 初始值为Map[String, String]()

    def fold[A1 >: A](z: A1)(op: (A1, A1) => A1): A1 = foldLeft(z)(op)
    

    fold 定义我们可以看到编译器期望Option[Map[String, String]](折叠中的每个值)和Map[String, String](初始化值)应该是相同的类型。

    如果您检查 Option 层次结构,您将看到:

    Option -> Product -> Equals

    对于Map,我们有以下内容:

    Map -> GenMap -> GenMapLike -> Equals(特征层次结构复杂,可能存在其他链)。

    所以我们可以看到最接近的常见类型是Equals

    谜题的第二部分是(_)

    编译器将其视为 lambda 的参数:

    val interests = x => /*omited*/.fold(Map[String, String]())(x)
    

    我们看到x(A1, A1) => A1。在我们的例子中是:

    (Equals, Equals) => Equals
    

    fold 的结果是A1,这也是Equals

    因此 lambda 类型为:

    ((Equals, Equals) => Equals) /*<< arg*/ => /*result >>*/ Equals
    

    更新:

    为了解决你的问题,我认为你应该使用:

    .flatten.reduce(_ ++ _)
    

    【讨论】:

    • 啊,所以使用的是 Seq.fold 而不是 Option.fold?使用 Option.fold/reduce 的方式是否扁平化?
    • 没有。 flatten 用于过滤Nones 并从Somes 中提取值。之后将提取的Map[String, String]s 连接起来。
    【解决方案2】:

    当您将 ProductEquals 之类的特征作为语句的推断类型时,您通常可以打赌您在某些高阶函数中存在类型不匹配。您通常不会得到AnyAnyRef,因为只有当某些类型的集合没有任何共同点 时才会出现这些情况。让我印象深刻的一件事是,您对fold 的第二个参数只接受一个参数。令人惊讶的是,它会进行类型检查,但它可能会为您提供您不期望的类型。

    我认为你想做的更像是:

    val interestRegEx = """(\w+) Interests \((.+ Interest)\)""".r
    val singleAttributes = Seq("Sport Interests (Some Interest):Running,Swimming","Something else:True")
    val interests = singleAttributes.map { x =>
      // e.g. ("Sport Interests (Some Interest)", "Running,Swimming") should
      // result in ("Sport Running" -> "Some Interest", "Sport Swimming" -> "Some Interest")
      val attVal = x.split(':')
    
      attVal(0) match {
        case interestRegEx(interestProduct, interestAmount) =>
          attVal(1).split(",").map { i =>
            Map(s"$interestProduct $i" -> interestAmount)
          }.reduce(_ ++ _)
        case _ => Map.empty
      }
    }.reduce(_ ++ _)
    

    为此我得到了:

    scala> interests
    res0: scala.collection.immutable.Map[_ <: String, String] = Map(Sport Running -> Some Interest, Sport Swimming -> Some Interest)
    

    我摆脱了 Option 在您的正则表达式匹配中包装 Maps。由于您计划合并地图,因此不妨使用空地图作为您的不匹配案例。然后我使用reduce 而不是fold 进行最后一场比赛,就像你在内部map 中所做的那样。

    【讨论】:

    • java.io.Serializable 是另一种类型推断喜欢偶尔发现的常见超类型。
    猜你喜欢
    • 2016-07-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-13
    • 1970-01-01
    • 1970-01-01
    • 2020-10-05
    相关资源
    最近更新 更多