Scala: получение промежуточных результатов складки
Я неоднократно сталкивался с проблемой поддержания состояния на всей карте. Представьте себе следующую задачу:
Учитывая список [Int], сопоставьте каждый элемент с суммой всех предыдущих элементов и себя.
Таким образом, 1,2,3 становится 1, 1 + 2, 1 + 2 + 3.
Одно из решений, которое я придумал, это:
scala> val a = 1 to 5
a: scala.collection.immutable.Range.Inclusive with scala.collection.immutable.Range.ByOne = Range(1, 2, 3, 4, 5)
scala> a.foldLeft(List(0)){ case (l,i) => (l.head + i) :: l }.reverse
res3: List[Int] = List(0, 1, 3, 6, 10, 15)
Но почему-то я чувствую, что должно быть более простое решение.
Ответы
Ответ 1
Вы пытаетесь вычислить последовательность частичных сумм.
Общая операция вычисления таких накоплений не fold
, а scan
, хотя scan
выражается через fold
способом, который вы показывали ( и fold
на самом деле является последним элементом списка, созданным scan
).
Что касается Scala, я приведу этот пример
scala> scanLeft(List(1,2,3))(0)(_ + _)
res1: List[Int] = List(0, 1, 3, 6)
Ответ 2
@Dario дал ответ, но просто добавив, что библиотека scala предоставляет scanLeft:
scala> List(1,2,3).scanLeft(0)(_ + _)
res26: List[Int] = List(0, 1, 3, 6)
Ответ 3
Ответы scan
являются лучшими, но стоит отметить, что можно сделать снимок более привлекательным и/или быть короче, чем в вашем вопросе. Во-первых, вам не нужно использовать сопоставление шаблонов:
a.foldLeft(List(0)){ (l,i) => (l.head + i) :: l }.reverse
Во-вторых, обратите внимание, что foldLeft имеет аббревиатуру:
(List(0) /: a){ (l,i) => (l.head + i) :: l }.reverse
В-третьих, обратите внимание, что вы можете, если хотите, использовать коллекцию, которая может эффективно добавляться, чтобы вам не нужно было отменить:
(Vector(0) /: a){ (v,i) => v :+ (v.last + i) }
Итак, хотя это не так компактно, как scanLeft
:
a.scanLeft(0)(_ + _)
все еще не так уж плохо.
Ответ 4
Мне нравится складываться так же, как и все остальные, но менее ответ FP очень краток и читабельен:
a.map{var v=0; x=>{v+=x; v}}