使用List of Tuples时,Scala会混淆编译时错误

时间:2012-07-16 18:15:29

标签: scala functional-programming

我正在通过Ninety-Nine Scala Problems了解更多Scala。我在P12上并为此问题编写了以下解决方案。

def decode(l : List[Tuple2[Int,Symbol]]) : List[Symbol] 
            = l foldLeft(List[Symbol]()) { (symbols:List[Symbol], e:Tuple2[Int, Symbol]) => symbols ::: List(e._2) }

我收到以下编译错误。

 error: type mismatch;
 found   : (List[Symbol], (Int, Symbol)) => List[Symbol]
 required: Int
                        = l foldLeft(List[Symbol]()) { (symbols:List[Symbol], e:
Tuple2[Int, Symbol]) => symbols ::: List(e._2) }

导致编译错误的原因是什么?

Scala版本:Scala代码运行版版本2.10.0-M3 - 版权所有2002-2011,LAMP / EPFL。

3 个答案:

答案 0 :(得分:4)

看起来你正在使用中缀foldLeft调用,你只需将其更改为:

def decode(l : List[Tuple2[Int,Symbol]]) : List[Symbol] 
        = l.foldLeft(List[Symbol]()) { (symbols:List[Symbol], e:Tuple2[Int, Symbol]) => symbols ::: List(e._2) }

注意“l.foldLeft”而不是“l foldLeft”,我怀疑编译器无法确定什么是参数。

答案 1 :(得分:3)

已经给出了解决方案,但我认为需要更多解释:

如果表达式位于操作员位置,则只允许留下括号和圆点。如果表达式具有<object> <method> <param>形式,则表达式位于运算符位置。对于包含多个显式参数列表foldLeft的方法,情况并非如此。因此,你必须写<list>.foldLeft(<init>)(<function>)。然而,Scala有一个特殊的规则可以解决这个问题 - 您可以插入另一组括号:(<list> foldLeft <init>) (<function>)。此外,还有另一种称为/:的方法,它是foldLeft的同义词,定义为def /:[B](z: B)(op: (B, A) => B): B = foldLeft(z)(op)。它允许您编写(<init> /: <list>) (<function>)。也许你刚才注意到第一个括号之间的符号是交换的 - 这是因为每个以冒号结尾的方法都是正确的 - 而不是左关联(further explanation)。

现在我想给你一些进一步重构的提示:

  • Tuple2[A, B]可以写成(A, B)
  • 您不必编写所有类型。他们中的一些人可以 - 而且应该 - 留下来清理你的代码(我知道你是一个初学者并想写这个。只是作为一个暗示......)。但是不要离开
  • 列表大多名称为xsys,因为这意味着“很多x”或“很多y”。这不是很重要但很常见
  • 您可以对参数进行模式匹配,以将其提取为易于阅读的名称:... { case (a, (b,c)) => ...}
  • 您的代码无法正常运行,因为它声明了该任务。您需要List.fill(<n>)(<elem>)
  • 之类的内容
  • 不要将元素附加到列表中,这是O(n)。隐式地:::是追加操作 - 请查看sources
  • 对于此任务,foldLeft不是最佳解决方案。 foldRight或同义词:\可能更有效,因为:::操作需要较少的元素进行复制。但我更喜欢flatMap(见下文),这是map+flatten
    • 您可以使用for-comprehension来解决这个问题,这通常很容易阅读。有关如何在内部实现理解的更多信息,请参阅this

所有示例解决方案中都包含:

object Test extends App {
  def decode1(l: List[Tuple2[Int, Symbol]]): List[Symbol] =
    l.foldLeft(List[Symbol]()) { (symbols: List[Symbol], e: Tuple2[Int, Symbol]) => symbols ::: List.fill(e._1)(e._2) }

  def decode2(xs: List[(Int, Symbol)]): List[Symbol] =
    (xs foldLeft List.empty[Symbol]) { case (xs, (n, s)) => xs ::: List.fill(n)(s) }

  def decode3(xs: List[(Int, Symbol)]): List[Symbol] =
    (xs foldRight List.empty[Symbol]) { case ((n, s), xs) => List.fill(n)(s) ::: xs }

  def decode4(xs: List[(Int, Symbol)]): List[Symbol] =
    (List.empty[Symbol] /: xs) { case (xs, (n, s)) => xs ::: List.fill(n)(s) }

  def decode5(xs: List[(Int, Symbol)]): List[Symbol] =
    xs flatMap { case (n, s) => List.fill(n)(s) }

  def decode6(xs: List[(Int, Symbol)]): List[Symbol] =
    for {
      (n, s) <- xs
      ys <- List.fill(n)(s)
    } yield ys

  val xs = List((4, 'a), (1, 'b), (2, 'c), (2, 'a), (1, 'd), (4, 'e))
  val ys = List('a, 'a, 'a, 'a, 'b, 'c, 'c, 'a, 'a, 'd, 'e, 'e, 'e, 'e)

  println("start testing")

  val tests = List[List[(Int, Symbol)] => List[Symbol]](decode1, decode2, decode3, decode4, decode5, decode6)

  for (t <- tests)
    assert(t(xs) == ys)

  println("finished")
}

答案 2 :(得分:1)

如果您使用foldLeft明确调用l.foldLeft,则错误消失:

def decode(l: List[Tuple2[Int,Symbol]]): List[Symbol] =
  l.foldLeft(List[Symbol]()){(symbols:List[Symbol], e:Tuple2[Int, Symbol]) =>
      symbols ::: List(e._2)}

查看this question的第一个答案,详细解释Scala的调用语法,该语法也涵盖了您的案例。