【问题标题】:Scala: type arguments do not conform to trait Subtractable's type parameter boundsScala:类型参数不符合 trait Subtractable 的类型参数界限
【发布时间】:2019-02-14 18:27:10
【问题描述】:

我是 scala 的新手,正在尝试编写一个函数,该函数返回给定字符串中每个字母的所有索引的映射。我的代码:

def group(string: String) = {
  val map = mutable.Map[Char, ListBuffer[Int]]()
  for (i <- string.indices) {
    val ch = string(i)
    if(map.contains(ch)) map(ch) += i
    else map += (ch -> ListBuffer(i))
  }
  map
}

当我尝试编译时出现错误:

错误:(14, 30) 类型参数 [?,Iterable[Any] with PartialFunction[Int with Char,Any] with scala.collection.generic.Subtractable[_ >: Int with Char <: anyval iterable with partialfunction char scala.collection.generic.subtractable>: Int with Char <: anyval iterable with partialfunction char scala.collection.generic.subtractable>: Int with Char <: anyval equals seq: iterable with partialfunction char trait subtractable scala.collection.generic.subtractable val v="for" string.indices>

循环的值似乎有问题。所以我添加到循环'true'的最后一行,现在一切正常:

def group(string: String) = {
  val map = mutable.Map[Char, ListBuffer[Int]]()
  for (i <- string.indices) {
    val ch = string(i)
    if(map.contains(ch)) map(ch) += i
    else map += (ch -> ListBuffer(i))
    true
  }
  map
}

我的代码有什么问题,我该如何解决? Scala 版本:2.12.6

【问题讨论】:

    标签: scala loops collections


    【解决方案1】:

    你不需要保留一个 ListBuffer 来保存索引,我们可以用一个新的附加索引列表来更新映射条目。

    def group(string: String): mutable.Map[Char, List[Int]] = {
    
        val map = mutable.Map.empty[Char, List[Int]]
    
        string.zipWithIndex.foreach { case (char: Char, index: Int) =>
            map += (char -> (index :: map.get(char).getOrElse(List())))
        }
    
        map
    }
    

    【讨论】:

      【解决方案2】:

      正如您在https://docs.scala-lang.org/tutorials/FAQ/yield.html 中看到的那样

      Scala 的“for comprehensions”是使用 foreach、map、flatMap、filter 或 withFilter 组合多个操作的语法糖。 Scala 实际上将 for 表达式转换为对这些方法的调用,因此任何提供它们的类或它们的子集都可以用于 for 推导式。

      所以,你的 for 循环

      for (i <- string.indices) {
        val ch = string(i)
        if(map.contains(ch)) map(ch) += i
        else map += (ch -> ListBuffer(i))
      }
      

      相当于

      string.indices.foreach(i => {
        val ch = string(i)
        if(map.contains(ch)) map(ch) += i
        else map += (ch -> ListBuffer(i))
      })
      

      foreach方法接口是

      def foreach[U](f: A => U): Unit
      

      map(ch) += i 返回ListBuffer,但map += (ch -&gt; ListBuffer(i)) 返回Map。而当编译器试图在foreach 参数f: Int =&gt; U 中识别U 时,它会在ListBufferMap 之间得到一些东西,并且不会编译它。

      另外,如果你不在某处使用 if-else 表达式,编译器不会检查结果类型。

      您可以像这样重写代码来修复您的代码

      def group(string: String) = {
          val map = mutable.Map[Char, ListBuffer[Int]]()
      
          def update(i: Int): Unit = {
              val ch = string(i)
              if(map.contains(ch)) map(ch) += i
              else map += (ch -> ListBuffer(i))
          }
      
          for (i <- string.indices) update(i)
          map
      }
      

      但最好使用标准方法

      def group(string: String) = string.toCharArray.zipWithIndex.groupBy(_._1).mapValues(_.map(_._2))
      

      【讨论】:

        【解决方案3】:

        您的方法未编译的原因是您返回的类型与 if-else 表达式完全不同。一个返回ListBuffer[Int],另一个返回Map[Char, ListBuffer[Int]]

        你想要的是:

        def group(string: String): mutable.Map[Char, ListBuffer[Int]] = {
          val map = mutable.Map[Char, ListBuffer[Int]]()
          for (i <- string.indices) {
            val ch = string(i)
            if (map.contains(ch)) {
              map(ch) += i
              map
            } else map += (ch -> ListBuffer(i))
          }
          map
        }
        

        没有可变MapListBuffer 的另一种方法可能是:

        def group(s: String): Map[Char, Int] = {
          s.split("\\W+")
           .flatten
           .zipWithIndex
           .groupBy { case (char, _) => char }
           .mapValues { arr => arr.map(_._2) }
        }
        

        【讨论】:

        • 不,我不想计算字符串中 Char 的出现次数。我的方法可以正常工作并给出正确的结果。问题是为什么没有“真”字符串就不能工作?
        • 是否需要从if-else 返回相同的类型?我可以写类似 for (i &lt;- 0 to 10) if (i % 2 == 0) 1 else Array(1) 的东西。 if-else 返回完全不同的类型,但是这里没有错误
        • @Andrew 你可以这样写,然后从那个表达式产生的值就是Any,你不能用它做很多事情。没有人会强迫您在这样的表达式中返回相同的类型,因为不需要返回类型(更具体地说,编译器足够聪明,可以找出两个表达式的必须共同的超类型),而在您的 group 方法中,合同要求Map[Char, Int],但您对此并不满意。
        • 对不起,我不明白你... Map[Char, Int] 应该在哪里? If-else 返回 ListBuffer[Int]Map[Char, ListBuffer[Int]]。所以产生的值可能是Any
        • @Andrew Map[Char, Int] is only expected in my group` 版本,不是你的。您使用for 的方式只是foreach 上的语法糖,编译器无法推断该foreach 调用中函数的返回类型。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-09-30
        • 2019-12-18
        • 1970-01-01
        • 2019-06-07
        • 1970-01-01
        相关资源
        最近更新 更多