2011-08-22 38 views
15

例如,假設我有有沒有辦法在Scala for循環中處理最後一種情況?

for (line <- myData) { 
    println("}, {") 
    } 

是否有一種方式來獲得最後一行打印

println("}") 
+0

正如@ Nicolas的答案已經指出的那樣,如果您可以重構所需的行爲以針對* first *迭代採取不同的行爲,而不是最後一個。識別第一次迭代是微不足道的。 –

+4

+1使用scala –

回答

18

在進一步討論之前,我'd建議你避免println爲理解。它有時可以用於追蹤集合中間發生的錯誤,但否則會導致代碼難以重構和測試。

更一般地說,如果您可以限制哪裏發生任何副作用,生活通常會變得更容易。因此,而不是:

for (line <- myData) { 
    println("}, {") 
} 

你可以寫:

val lines = for (line <- myData) yield "}, {" 
println(lines mkString "\n") 

我還要在這裏採取的猜測,你想在輸出的每一行的內容!

val lines = for (line <- myData) yield (line + "}, {") 
println(lines mkString "\n") 

雖然你會是好還是如果你只是使用mkString直接 - 這就是它是!

val lines = myData.mkString("{", "\n}, {", "}") 
println(lines) 

注意我們如何首先生產String,然後再打印在一個單一的操作。這種方法可以很容易地分解成單獨的方法,並用於在您的類上實現toString,或者在測試中檢查生成的String。

6

您可以採取TraversableOnce特質作爲例子的addString功能。

def addString(b: StringBuilder, start: String, sep: String, end: String): StringBuilder = { 
    var first = true 

    b append start 
    for (x <- self) { 
    if (first) { 
     b append x 
     first = false 
    } else { 
     b append sep 
     b append x 
    } 
    } 
    b append end 

    b 
} 

在你的情況下,隔膜是}, {和到底是}

+0

+1。很高興看到它是如何在標準庫中完成的。 –

28

可以重構代碼趁內置mkString

scala> List(1, 2, 3).mkString("{", "}, {", "}") 
res1: String = {1}, {2}, {3} 
2

如果你不希望使用內置mkString功能,你可以像

for (line <- lines) 
    if (line == lines.last) println("last") 
    else println(line) 

UPDATE:正如在評論中提到didierd,該解決方案是錯誤的,因爲最後一個值可以發生好幾次,他在他的answer中提供了更好的解決方案。

這是罰款Vectors,因爲last功能以「有效恆定的時間」對他們來說,作爲Lists,它需要線性時間,所以你可以使用模式匹配

@tailrec 
def printLines[A](l: List[A]) { 
    l match { 
    case Nil => 
    case x :: Nil => println("last") 
    case x :: xs => println(x); printLines(xs) 
    } 
} 
+1

遞歸解決方案很好。除非你知道最後一個元素的值不在別的地方,否則用== lines.last的那個是錯誤的。 –

+0

@didierd謝謝,我錯過了。我在答案中指出了錯誤。 – 4e6

+0

我試過用'eq'而不是'==',它仍然不起作用 - 字符串必須緩存,這有點痛苦 –

12

我完全同意之前所說的關於使用mkstring,並區分第一次迭代而不是最後一次迭代。你仍然需要區分最後,斯卡拉集合有一個init方法,它會返回除最後一個元素外的所有元素。 所以你可以做

for(x <- coll.init) workOnNonLast(x) 
workOnLast(coll.last) 

initlast是排序的頭部和尾部的反面,這是第一個和所有,但第一個)。但是請注意,根據結構不同,它們可能會很昂貴。在Vector,他們都快。在List上,雖然頭部和尾部基本上是空閒的,但是initlast在列表長度上都是線性的。 headOptionlastOption可以幫助你時,集合可能是空的,由

for (x <- coll.lastOption) workOnLast(x) 
1

其他的答案是理所當然指出mkString更換workOnlast,以及數據的正常量我也使用它。

但是,mkString通過StringBuilder構建(累積)最終結果內存。這並不總是令人滿意的,這取決於我們擁有的數據量。在這種情況下,如果我們想要的只是「打印」,我們不需要先建立大的結果(也許我們甚至想避免這種情況)。

考慮這個輔助函數的實現:

def forEachIsLast[A](iterator: Iterator[A])(operation: (A, Boolean) => Unit): Unit = { 
    while(iterator.hasNext) { 
    val element = iterator.next() 
    val isLast = !iterator.hasNext // if there is no "next", this is the last one 
    operation(element, isLast) 
    } 
}  

它遍歷所有元素,並調用operation傳遞每個元件又與一個布爾值。如果傳遞的元素是最後一個,則值爲true

在你的情況下,它可以像這樣使用:

forEachIsLast(myData) { (line, isLast) => 
    if(isLast) 
    println("}") 
    else 
    println("}, {") 
} 

我們有以下的優點在這裏:

  • 它運行的每個元素上,一個接一個,而不必積累的結果記憶(除非你想)。
  • 因爲它不需要將整個集合加載到內存中來檢查它的大小,所以只需要詢問Iterator是否耗盡就足夠了。您可以從大文件或網絡中讀取數據等。
+2

或者稍微優雅些,也許:'forEachIsLast'可以是'val it = seq。toIterator it.foreach {operation(_,it.hasNext)}' –

+0

非常感謝@TheArchetypalPaul!根據您的建議編輯我的答案以使用實現。 –

相關問題