Scala - 模式匹配和For循环问题

时间:2013-10-28 22:07:32

标签: scala for-loop pattern-matching

我正在尝试解决S-99: Ninety-Nine Scala Problems

的问题12
  

给定按问题P10中指定生成的游程长度代码列表,   构建其未压缩版本。例如:

scala> decode(List((4, 'a), (1, 'b), (2, 'c), (2, 'a), (1, 'd), (4, 'e)))
res0: List[Symbol] = List('a, 'a, 'a, 'a, 'b, 'c, 'c, 'a, 'a, 'd, 'e, 'e, 'e, 'e)

我试图模式匹配列表中的元素,然后使用for循环来连接char,但我在第5行遇到了以下编译错误:

type mismatch;  found   : scala.collection.immutable.IndexedSeq[List[A]]  required: List[A]

1 def decode[A](xs: List[(Int, A)]) : List[A] = xs match {
2     case Nil => Nil
3     case x :: xs => {
4                    for {
5                       i <- 1 to x._1
6                    }  yield (x._2) :: decode(xs)
7                   }
8 }

抱歉,我开始使用Scala。有人可以解释为什么会这样,以及如何解决它?

5 个答案:

答案 0 :(得分:4)

你很亲密 - 只是几个问题。这是我提出的固定版本:

def decode[A](xs: List[(Int, A)]) : List[A] = xs match {
   case Nil => Nil
   case x :: xs => (for {
                       i <- 1 to x._1
                    } yield (x._2)).toList ::: decode(xs)
}

第一个 - 也许是最重要的 - 是围绕for-yield的额外括号。如果没有这个,你试图产生(x._2) :: decode(xs),而不仅仅是(x._2)(为了弥补这一点,可以省略整个案例的{}。)

接下来,for-yield导致IndexedSeq而不是List,所以我强制转换为List(你可以用各种方式处理它,这只是最方便的)。

最后,连接到decode(xs)生成的列表需要:::运算符(您也可以使用++)而不是::(前缀为单个条目,而不是子列表)。

答案 1 :(得分:4)

主要问题是您用于连接列表的运算符 - ::仅用于将单个元素添加到列表中,因此在您的代码中,您试图预先添加yield的结果(这本身就是序列)到List[A]并因此得到类型不兼容。这是一个可以使用的修改版本 - 它使用运算符++:,它可以用于将两个序列连接在一起。我还将yield移到了一个单独的语句中,否则你需要围绕yield的括号,以便++:处理yield的完整结果,而不是每个元素(这将再次)由于类型不匹配而无法编译。)

def decode[A](xs: List[(Int, A)]) : List[A] = xs match {
  case Nil => Nil
  case x :: xs => {
    val repeatedElems = for {
      i <- 1 to x._1
    }  yield (x._2)
    repeatedElems ++: decode(xs)
  }
}

答案 2 :(得分:2)

其他答案非常好但我认为使用List.fill生成解码列表需要的语法更少,并且与for-yield表达式相比更容易理解。

def decode[A](xs: List[(Int, A)]) : List[A] = xs match {
   case Nil => Nil
   case x :: xs => List.fill(x._1)(x._2) ::: decode(xs)
}

输出:

scala> decode(List((4, 'a), (1, 'b), (2, 'c), (2, 'a), (1, 'd), (4, 'e)))
res0: List[Symbol] = List('a, 'a, 'a, 'a, 'b, 'c, 'c, 'a, 'a, 'd, 'e, 'e, 'e, 'e)

答案 3 :(得分:0)

以下是Brian的答案的略微修改版本。分解元组使代码更具可读性:

def decode[A](xs: List[(Int, A)]) : List[A] = xs match {
   case Nil => Nil
   case (count, letter) :: xs => List.fill(count)(letter) ::: decode(xs)
}

答案 4 :(得分:0)

另一种方法是使用map:

def decode[A](l: List[(Int, A)]): List[A] = {
  val l1: List[List[A]] = l map { e =>
    List.fill(e._1)(e._2)
  }
  l1.flatten
}