2010-07-11 10 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))