首页 > 解决方案 > 一个“简单”的 Scala 问题,但我花了很长时间来调试

问题描述

请检查上面的两段脚本。

genComb4(lst)自从我在理解z <- genComb4(xs)之前开始工作;不起作用,因为我为了理解而更改了这两行的顺序。i <- 0 to x._2genComb(lst)

我花了将近半天的时间才找到这个错误,但我无法自己解释。你能告诉我为什么会这样吗?

非常感谢您提前。

// generate combinations
val nums = Vector(1, 2, 3)
val strs = Vector('a', 'b', 'c')
val lst: List[(Char, Int)] = strs.zip(nums).toList

def genComb4(lst: List[(Char, Int)]): List[List[(Char, Int)]] = lst match {
  case Nil => List(List())
  case x :: xs =>
    for {
      z <- genComb4(xs)  // correct
      i <- 0 to x._2     // correct
    } yield ( (x._1, i) :: z)
}
genComb4(lst)

def genComb(lst: List[(Char, Int)]): List[List[(Char, Int)]] = lst match {
  case Nil => List(List())
  case x :: xs =>
    for {
      i <- (0 to x._2)  // wrong
      z <- genComb(xs)  // wrong
    } yield ( (x._1, i) :: z)
}
genComb(lst)

标签: scalafor-comprehension

解决方案


这是因为for comprehension. 当您for-comprehension从 line:开始时,i <- (0 to x._2)将结果容器的类型设置为IndexedSeq但如果第一行是z <- genComb4(xs)结果容器的类型,List请看一下:

val x = 'a' -> 2
val indices: Seq[Int] = 0 to x._2
val combs: List[List[(Char, Int)]] = genComb4(List(x))

// indexed sequence
val indicesInFor: IndexedSeq[(Char, Int)] = for {
  i <- 0 to x._2
} yield (x._1, i)

// list 
val combsInFor: List[List[(Char, Int)]] = for {
  z <- genComb4(List(x))
} yield z

所以为了让你的第二种情况有效,你应该投(0 to x._2).toList

val indicesListInFor: List[(Char, Int)] = for {
  i <- (0 to x._2).toList
} yield (x._1, i)

结果代码应为:

def genComb(lst: List[(Char, Int)]): List[List[(Char, Int)]] = lst match {
  case Nil => List(List())
  case x :: xs =>
    for {
      i <- (0 to x._2).toList
      z <- genComb(xs)
    } yield ( (x._1, i) :: z)
}
genComb(lst)

您应该记住scala 集合的理解继承中的起始线类型。如果继承规则不能将下一个类型转换为第一个表达式行类型,您应该自己处理它。for-comprehension

好的做法是打开for-expression,flatMapmap函数withFilter,然后你会发现输入错误或其他更快的东西。

有用的链接:


推荐阅读