2010-07-11 8 views
20

Tengo una lista de días del mes:En Scala, ¿cómo doblo una Lista y devuelvo los resultados intermedios?

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

necesito para devolver una lista con la suma acumulada de días:

val cumDays = List(31, 59, 90) 

he pensado en utilizar el operador veces :

(0 /: days)(_ + _) 

pero esto solo devolverá el resultado final (365), mientras que necesito la lista de resultados intermedios.

De todos modos, ¿puedo hacerlo elegantemente?

Respuesta

44

Scala 2.8 tiene los métodos scanLeft y scanRight que hacen exactamente eso.

Para 2.7 se puede definir su propio scanLeft así:

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 

y luego usarlo como esto:

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

Gracias, pero solo puedo usar Scala 2.7 por ahora ... – lindelof

+0

¡Finalmente! Realmente no podía creer que este problema común (?) No tuviera una abstracción existente. Gracias por señalar esto. – MEMark

+0

También 'List (1, 2, 3) .scanLeft (0) (_ + _)'. – Jus12

2

se pliegan en una lista en lugar de un número entero. Use par (lista parcial con los valores acumulados, acumulador con la última suma) como estado en el doblez.

+0

Interesante, ¿alguna muestra de código? – lindelof

+0

Básicamente la definición de escaneo dada por sepp2k. – Mau

0

Obras en 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

doblar la lista en una nueva lista. En cada iteración, agregue un valor que es la suma del encabezado + la siguiente entrada. A continuación, invierta todo el asunto.

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

No estoy seguro de por qué todo el mundo parece insistir en el uso de algún tipo de plegado, mientras que, básicamente desea asignar los valores a los valores acumulados ...

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

Porque si hay una solución que utiliza un pliegue y otra solución que usa variables variables, la mayoría de las personas (con un fondo FP) elegiría el doblez. – sepp2k

+8

No veo ningún problema con esto: la var no está expuesta, la solución es fácil de entender, corta y fácil de leer, y el cálculo es eficiente. Claro, el uso de mutabilidad debe ser limitado y no exagerado, pero la inmutabilidad es solo una herramienta y no un dogma, al menos en cualquier idioma que ofrezca mutabilidad. – Landei

+4

La definición de 'foldLeft' usa' var's, por lo que usarlos aquí es muy bueno. A veces, los datos mutables son más eficientes y claros, y este es un buen ejemplo. –

5

Simplemente puede realizar que:

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

también puede crear una clase monoid que concatena dos listas al tiempo que añade a la segunda el último valor de la primera. No hay mutables ni pliegues involucrados:

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