2014-03-05 23 views
4

在我的命令式Scala代碼,我有一個算法:如何將「餘下的」算法轉換爲功能樣式?

def myProcessor(val items: List) { 
    var numProcessed = 0 
    while(numProcessed < items.size) { 
    val processedSoFar = items.size - numProcessed 
    numProcessed += processNextBlockOfItems(items, processedSoFar) 
    } 
} 

我想保持「塊處理」的功能,而不是僅僅做一個「takeWhile」的項目名單上。我怎樣才能以功能性風格重寫?

+0

'高清myProcessor(VAL項目:名單){myProcessor(項目,items.size())}';並將當前方法更改爲具有應處理的當前項目數量「int」的額外參數,而不是在一段時間內調用,而是遞歸調用。在你看來,這種功能足夠嗎? – amit

+0

@amit所以它會永遠運行? –

+0

@HunterMcMillen當然不是,當numProcessed <0 – amit

回答

1

所以,這取決於你認爲什麼是更多的功能,但這裏有一個版本,不帶「變種」

def myProcessorFunctional(items: List[Int]) { 
    def myProcessorHelper(items: List[Int], numProcessed: Int) { 
     if (numProcessed < items.size) { 
     val processedSoFar = items.size - numProcessed 
     myProcessorHelper(items, 
      numProcessed + processNextBlockOfItems(items, processedSoFar)) 
     } 
    } 
    myProcessorHelper(items, 0) 
    } 

(使它INTS的名單只是爲了簡單起見,將容易使它使用一個通用的列表)

我不得不說它是我不介意可變變量的那些情況之一 - 很明顯,沒有引用它可以逃避方法。

但正如我在上面的評論中所說的,無論如何,processNextBlockOfItems本質上是非功能性的,因爲它被稱爲副作用。一個更有效的方式是讓它返回到目前爲止的處理狀態,並且這個狀態將在隨後的調用中更新(並返回)。現在,如果你在處理兩個不同的項目名單中間,你就必須processNextBlockOfItems內維護兩個不同的部分處理的狀態的問題...

後來:

仍然忽略狀態問題,如果processNextBlockOfItems總是處理傳遞給它的項目列表的第一個塊,則返回其未處理的剩餘項目(如果使用List,這很方便和高效,所以我想知道爲什麼你會「重新使用指示)。

這會產生這樣的:

def myProcessorMoreFunctional(items: List[Int]) { 
    if (!items.isEmpty) { 
     myProcessorMoreFunctional(processNextBlockOfItems(items)) 
     } 
    } 
+0

在重構努力中耗盡時間以嘗試避免副作用,但幸運的是隻會有一個項目列表;副作用需要顯示輸出。最後一個建議,返回剩餘的項目,我認爲當然是一個很好的優化,我用它。謝謝! – sdanzig

5

您需要將其更改爲遞歸式的,其中你在每個循環

@tailrec 
def myProcessor(items: List[A], count: Int = 0): Int = items match{ 
    case Nil => count 
    case x :: xs => 
    processNextBlockOfItems(items, count) 
    myProcessor(xs, count + 1) 
} 

假設「processedSoFar」不是一個指數的「狀態」,「通行證」。如果你可以用列表的當前「頭」的工作:

@tailrec 
def myProcessor(items: List[A], count: Int = 0): Int = items match{ 
    case Nil => count 
    case x :: xs => 
    process(x) 
    myProcessor(xs, count + 1) 
} 

其中process只會處理List目前「頭」。

+0

是的,正如我建議的那樣,我不想將它改爲一次處理一個,部分原因是懶惰,但也意圖在其他地方不改變過多的代碼。你不同意一個「takeWhile」的語法是否更清晰,如果它是一次一個? – sdanzig

+0

實際上,我想我注意到了一個問題......您的代碼忽略了每個塊中處理的項目數量。它是processNextBlockOfItems,而不是processNextItem。如果我沒有弄錯,這可以通過用「count + returnValueFromBlockProcessing」替換「count + 1」來解決。 – sdanzig

+0

爲什麼在第一種情況下需要x :: xs?看起來我想將「未處理的項目」的子數組傳遞給myProcessor,而不僅僅是「刪除了一個的列表」。我發現只是傳遞一個索引列表而不是「剩餘的項目」,而只處理索引......嗯......我想我會支持一個更好的匹配答案這個問題,儘管我很欣賞你最初的迴應。 – sdanzig