我正在尝试解决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。有人可以解释为什么会这样,以及如何解决它?
答案 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
}