2010-07-11 9 views
20

我有天在本月的列表:在斯卡拉,我如何摺疊列表並返回中間結果?

val days = List(31, 28, 31, ...) 

我需要與天的累計總和返回一個列表:

val cumDays = List(31, 59, 90) 

我想到了用摺疊操作的:

(0 /: days)(_ + _) 

但這隻會返回最終結果(365),而我需要中間結果列表。

無論如何,我能做到這一點優雅?

回答

44

斯卡拉2.8所擁有的方法scanLeftscanRight它這樣做。

2.7,你可以定義自己的scanLeft這樣的:

def scanLeft[a,b](xs:Iterable[a])(s:b)(f : (b,a) => b) = 
    xs.foldLeft(List(s))((acc,x) => f(acc(0), x) :: acc).reverse 

,然後用它是這樣的:

scala> scanLeft(List(1,2,3))(0)(_+_) 
res1: List[Int] = List(0, 1, 3, 6) 
+0

謝謝,但我現在只能使用Scala 2.7 ... – lindelof 2010-07-11 23:07:09

+0

最後!我真的不能相信這個共同的問題沒有現成的抽象。感謝您指出了這一點。 – MEMark 2013-04-21 13:25:23

+0

另外'List(1,2,3).scanLeft(0)(_ + _)'。 – Jus12 2014-03-16 19:49:51

2

摺疊成一個列表,而不是一個整數。使用對(部分列表中的累加值,累加器和最後一筆和數)作爲摺疊狀態。

+0

有趣的,任何代碼示例? – lindelof 2010-07-11 23:07:29

+0

基本上由sepp2k給定的掃描定義。 – Mau 2010-07-11 23:24:38

0

文選2.7.7:

def stepSum (sums: List [Int], steps: List [Int]) : List [Int] = steps match { 
    case Nil => sums.reverse.tail             
    case x :: xs => stepSum (sums.head + x :: sums, steps.tail) } 

days 
res10: List[Int] = List(31, 28, 31, 30, 31) 

stepSum (List (0), days) 
res11: List[Int] = List(31, 59, 90, 120, 151) 
1

摺疊列表成爲一個新列表。在每次迭代中,附加一個值,該值是頭+下一個輸入的總和。然後扭轉整個事情。

scala> val daysInMonths = List(31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31) 
daysInMonths: List[Int] = List(31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31) 

scala> daysInMonths.foldLeft(Nil: List[Int]) { (acc,next) => 
    | acc.firstOption.map(_+next).getOrElse(next) :: acc  
    | }.reverse            
res1: List[Int] = List(31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365) 
14

我不知道爲什麼每個人似乎都堅持使用某種摺疊式的,而你基本上要值映射到累積值...

val daysInMonths = List(31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31) 

val cumulated = daysInMonths.map{var s = 0; d => {s += d; s}} 

//--> List[Int] = List(31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365) 
+4

因爲如果有一個使用摺疊的解決方案和另一個使用可變變量的解決方案,大多數人(具有FP背景)都會選擇摺疊。 – sepp2k 2010-07-12 09:59:09

+8

我沒有看到任何問題:var不暴露,解決方案易於理解,簡短易讀,計算效率高。當然,可變性的使用應該是有限的,而不是過度的,但不變性只是一種工具而不是教條 - 至少在任何提供可變性的語言中。 – Landei 2010-07-12 11:30:49

+4

'foldLeft'的定義使用'var's,所以在這裏使用它們是很好的IMO。有時可變數據更高效更清晰,這是一個很好的例子。 – 2011-07-24 00:10:20

5

你可以簡單地進行它:

daysInMonths.foldLeft((0, List[Int]())) 
        {(acu,i)=>(i+acu._1, i+acu._1 :: acu._2)}._2.reverse 
1

您還可以創建連接兩個列表,同時增加了第二個從第一的最後一個值幺類。沒有可變參數和摺疊參與:

case class CumSum(v: List[Int]) { def +(o: CumSum) = CumSum(v ::: (o.v map (_ + v.last))) } 
defined class CumSum 

scala> List(1,2,3,4,5,6) map {v => CumSum(List(v))} reduce (_ + _) 
res27: CumSum = CumSum(List(1, 3, 6, 10, 15, 21))