2012-07-16 62 views
0

我正在通过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 - Copyright 2002-2011,LAMP/EPFL。

回答

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”,我怀疑编译器可以不完全决定什么是什么参数。

+0

感谢肖恩的answer.Are那里有没有什么好的文档,链接,博客文章等这件事吗? – 2012-07-16 18:29:09

+0

这里有一些条目已经围绕一些: http://stackoverflow.com/questions/5592642/when-to-use-parenthesis-in-scala-infix-notation 我会说一条经验法则只有在有明确的理由时才能做到(如“4 + 4”情况)。 – 2012-07-16 18:31:54

+0

库尔。再次感谢肖恩。 – 2012-07-16 18:34:25

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的调用语法的一个非常详细的解释也覆盖你的情况。

3

该解决方案已经给出的,但我认为这是一个需要更多的解释:

你只能离开括号和点,如果你的表现是在运营商的地位。如果表达式的格式为<object> <method> <param>,则表达式在运算符位置。对于包含多个显式参数列表的方法,情况并非如此foldLeft。因此你必须写<list>.foldLeft(<init>)(<function>)。尽管如此,斯卡拉有一个特殊的规则来解决这个问题 - 你可以插入另一组圆括号:(<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
    • 您可以使用一个换理解,以解决这个问题,这往往会容易阅读。有关更多信息,请参阅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") 
} 
+0

不错! ''decode5''是我的最爱。 – 2012-07-16 21:19:12

+0

谢谢大家的解释。同时,感谢您宝贵的提示和技巧:) – 2012-07-16 23:20:29