在Scala中,如何折叠List并返回中间结果?

时间:2010-07-11 22:48:26

标签: scala

我有一个月份的日期列表:

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

我需要返回一个累积天数的List:

val cumDays = List(31, 59, 90)

我想过使用折叠运算符:

(0 /: days)(_ + _)

但这只会返回最终结果(365),而我需要中间结果列表。

无论如何,我可以优雅地做到这一点?

8 个答案:

答案 0 :(得分:53)

Scala 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)

答案 1 :(得分:17)

我不确定为什么每个人似乎都坚持使用某种折叠,而你基本上想要将值映射到累积值......

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)

答案 2 :(得分:6)

您可以简单地执行它:

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

答案 3 :(得分:2)

折叠成列表而不是整数。使用对(具有累计值的部分列表,具有最后总和的累加器)作为折叠中的状态。

答案 4 :(得分: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)

答案 5 :(得分:1)

您还可以创建一个连接两个列表的monoid类,同时将第二个列表中的最后一个值添加到第二个列表中。没有变数,也没有折叠:

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))

答案 6 :(得分: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)

答案 7 :(得分:0)

对于任何

val s:Seq[Int] = ...

您可以使用其中之一:

s.tail.scanLeft(s.head)(_ + _)
s.scanLeft(0)(_ + _).tail

或其他答案中提出的折痕,但... 请注意,Landei的解决方案很棘手,应避免使用。

注意

s.map { var s = 0; d => {s += d; s}} 
//works as long `s` is strict collection

val s2:Seq[Int] = s.view //still seen as Seq[Int]
s2.map { var s = 0; d => {s += d; s}} 
//makes really weird things! 
//Each value'll be different whenever you'll access it!

我应该在兰代的答案下面警告此评论,但我不能:(。