2012-07-16 58 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