В Scala, как мне свернуть список и вернуть промежуточные результаты? - PullRequest
29 голосов
/ 12 июля 2010

У меня есть список дней в месяце:

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

Мне нужно вернуть список с совокупной суммой дней:

val cumDays = List(31, 59, 90)

Я подумалиспользования оператора сгиба:

(0 /: days)(_ + _)

, но это вернет только конечный результат (365), тогда как мне нужен список промежуточных результатов.

В любом случае, я могу сделать это элегантно?

Ответы [ 7 ]

53 голосов
/ 12 июля 2010

Scala 2.8 имеет методы scanLeft и scanRight, которые делают именно это.

Для 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)
17 голосов
/ 12 июля 2010

Я не уверен, почему все, кажется, настаивают на использовании какого-либо свертывания, в то время как вы в основном хотите сопоставить значения с накопленными значениями ...

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)
6 голосов
/ 28 октября 2012

Вы можете просто выполнить это:

daysInMonths.foldLeft((0, List[Int]()))
                     {(acu,i)=>(i+acu._1, i+acu._1 :: acu._2)}._2.reverse
2 голосов
/ 12 июля 2010

Свернуть в список вместо целого числа.В качестве состояния в сгибе используйте пару (неполный список с накопленными значениями, аккумулятор с последней суммой).

1 голос
/ 04 сентября 2016

Вы также можете создать моноидный класс, который объединяет два списка, добавляя во второй последнее значение из первого.Никакие изменчивые и не вовлеченные сгибы:

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))
1 голос
/ 12 июля 2010

Сложите свой список в новый список.На каждой итерации добавляйте значение, которое является суммой заголовка + следующего ввода.Затем полностью измените все это.

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)
0 голосов
/ 12 июля 2010

Работает на 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)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...