Scala:类型参数不符合特征Subtractable的类型参数范围

时间:2018-09-10 10:26:32

标签: scala loops collections

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

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 [Any ]与PartialFunction [Int with Char,Any] with scala.collection.generic.Subtractable [_>:Int with Char <:AnyVal,Iterable [Any] with PartialFunction [Int with Char,Any] with scala.collection.generic.Subtractable [_>:带有Char的整数<:AnyVal,等于]] {def seq:具有PartialFunction [Int与Char,Any]的Iterable [Any]}] {def seq:具有PartialFunction [具有Char,Any与Int的Iterable [Any] {def seq:Iterable [Any] with PartialFunction [Int with Char,Any]}}]不符合特征Subtractable的类型参数范围[A,+ Repr <:scala.collection.generic.Subtractable [A,Repr]]       val v = for(i <-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

3 个答案:

答案 0 :(得分:2)

未编译方法的原因是因为您返回的是与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) }
}

答案 1 :(得分:2)

您在https://docs.scala-lang.org/tutorials/FAQ/yield.html中看到

  

Scala的“ for comprehensions”是语法糖,用于使用foreach,map,flatMap,filter或withFilter组合多个操作。 Scala实际上将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 -> ListBuffer(i))返回Map。并且,当编译器尝试在U参数foreach中标识f: Int => 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))

答案 2 :(得分: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
}