2013-03-29 108 views
7

我有一個遞歸函數,我試圖讓內部遞歸部分(countR3)添加元素到隊列(agendascala.collections.mutable.Queue@tailrec。我的想法是,然後讓該功能的外部部分摺疊在議程上並總結結果。有沒有一種優雅的方式來摺疊不斷增長的scala.collections.mutable.Queue?

注意:這一個作業問題,因此我不想發佈整個代碼;然而,使得實現尾遞歸的是而不是作業的一部分。

下面是代碼的有關我的問題的一部分:

import scala.collection.mutable.Queue 

val agenda: Queue[Tuple2[Int, List[Int]]] = Queue() 

@tailrec 
def countR3(y: Int, x: List[Int]): Int = { 
    if (y == 0) 1 
    else if (x.isEmpty) 0 
    else if … 
    else { 
    agenda.enqueue((y - x.head, x)) 
    countR3(y, x.tail) 
    } 
} 
⋮ 
agenda.enqueue((4, List(1, 2))) 
val count = agenda.foldLeft(0) { 
    (count, pair) => { 
    val mohr = countR3(pair._1, pair._2) 
    println("count=" + count + " countR3=" + mohr) 
    count + mohr 
    } 
} 
println(agenda.mkString(" + ")) 
count 

幾乎似乎工作...問題是,它並沒有遍歷所有的項目列入議程,但它確實處理了其中的一些。您可以在下面的輸出看到這一點:[最終議程的六個項目中,只有前三個分別處理]

count=0 countR3=0 
count=0 countR3=0 
count=0 countR3=0 
(4,List(1, 2)) + (3,List(1, 2)) + (2,List(2)) + (2,List(1, 2)) + (1,List(2)) + (0,List(2)) 

我一般也感知突變的危害一個集合,當你在Java中迭代它時。但是隊列是一種不同顏色的馬。當然,我知道我可以簡單地寫勢在必行循環,就像這樣:

var count = 0 
while (!agenda.isEmpty) { 
    val pair = agenda.dequeue() 
    count += countR3(pair._1, pair._2) 
} 

這工作得很好,但是這是斯卡拉,我探索,看看是否有更功能優雅方式。

有什麼建議嗎?

回答

2

雖然可能不是完全習慣,你可以試試這個:

Stream.continually({ if (agenda.isEmpty) None else Some(agenda.dequeue()) }). 
    takeWhile(_.isDefined).flatten. 
    map({ case (x, y) => countR3(x, y) }). 
    toList.sum 
  • Stream.continually({ if (agenda.isEmpty) None else Some(agenda.dequeue()) })讓你包裹在Option[Tuple2[Int, List[Int]]]的隊列中的項目無限流。
  • 然後,takeWhile(_.isDefined)在遇到第一個None項目時,即當隊列用盡時,立即切斷序列。
  • 由於上次調用仍然產生一系列Option s,我們需要用flatten對它們進行解包。
  • map({ case (x, y) => countR3(x, y) })基本上將您的功能應用於每個項目。
  • 最後,toList強制評估一個流(這就是我們正在處理的),然後sum計算列表項的總和。

我覺得跟agenda.foldLeft(它處理的只有「一些」排隊項目)的問題是,我猜,它需要的當前排隊的項目(可能是不可變的)名單,因此不會受到物品在計算過程中添加的。

+0

不完全是慣用的,我同意,但它確實似乎適合更純粹的功能。很好的答案!有趣的是,議程。foldLeft'不僅僅處理最初入隊的項目,它似乎不是簡單地在初始隊列的副本上工作。也許當「最後」項目導致更多項目被添加到隊列中時,它會停止,或者類似的東西。 –

相關問題