A conventional mapping function has the signature A => B
, to transform a F[A]
into an F[B]
, for example a List[A]
into a List[B]
.
But what if do you do if the mapping function should carry some state along which is required for the computation of B
?
Say, the mapping function looks like this: (A, S) => (B, S)
, where S is the type of the State
. For each A
, the previously returned S
is passed into the mapping function, while initially a zero
element is provided for the state. The mapping function then returns a new state (along with the result) which is then again passed along with the next value, and so forth.
Of course, .map
is not powerful enough to do this, so the solution must be based on another operator.
For illustrative purposes, to give a concrete example, say I have a sequence of Ints
, and I want calculate the difference of each Int
to the previous Int
in that sequence. The implementation of the mapping function as described above would look like this:
def mapping(currentElement: Int, previousElement: Option[Int]): (Option[Int], Option[Int]) = {
(previousElement.map(currentElement - _), Some(currentElement))
}
The initial zero
value for previousElement
would be None
, and after the first element it would always be Some(currentElement)
. The result for each iteration would be a Some
of the current value minus the last value, except for the first element, where it is None
.
How could I transform, for example List(1, 4, 3)
to List(None, Some(3), Some(-1))
Using the mapping
function?
(Please note that the Int-subtraction example is purely for illustrative purposes, and the question's focus is a universal solution for the described type of operation.)
The Scala 2.13.x unfold()
method maintains a State that is carried forward similar to your example.
List.unfold((Option.empty[Int], List(1, 4, 3))){
case (prev, hd::tl) => Some((prev.map(hd.-), (Some(hd),tl)))
case (prev, Nil) => None
}
//res0: List[Option[Int]] = List(None, Some(3), Some(-1))
This is available on LazyList
and Iterator
so it can used to create a pseudo-infinite stream.