Skip to content

Commit 9ccba2c

Browse files
authored
Merge pull request #18 from adamgfraser/lazyFoldLeft
Add lazyFoldLeft
2 parents 1988581 + 615ef17 commit 9ccba2c

File tree

3 files changed

+47
-0
lines changed

3 files changed

+47
-0
lines changed

src/main/scala/scala/collection/decorators/IterableDecorator.scala

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,21 @@ class IterableDecorator[C, I <: IsIterable[C]](coll: C)(implicit val it: I) {
1919
def foldSomeLeft[B](z: B)(op: (B, it.A) => Option[B]): B =
2020
it(coll).iterator.foldSomeLeft(z)(op)
2121

22+
/** Lazy left to right fold. Like `foldLeft` but the combination function `op` is
23+
* non-strict in its second parameter. If `op(b, a)` chooses not to evaluate `a` and
24+
* returns `b`, this terminates the traversal early.
25+
*
26+
* @param z the start value
27+
* @param op the binary operator
28+
* @tparam B the result type of the binary operator
29+
* @return the result of inserting `op` between consecutive elements of the
30+
* collection, going left to right with the start value `z` on the left,
31+
* and stopping when all the elements have been traversed or earlier if
32+
* `op(b, a)` choose not to evaluate `a` and returns `b`
33+
*/
34+
def lazyFoldLeft[B](z: B)(op: (B, => it.A) => B): B =
35+
it(coll).iterator.lazyFoldLeft(z)(op)
36+
2237
/**
2338
* Right to left fold that can be interrupted before traversing the whole collection.
2439
* @param z the start value

src/main/scala/scala/collection/decorators/IteratorDecorator.scala

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,20 @@ class IteratorDecorator[A](val `this`: Iterator[A]) extends AnyVal {
5252
result
5353
}
5454

55+
def lazyFoldLeft[B](z: B)(op: (B, => A) => B): B = {
56+
var result = z
57+
var finished = false
58+
while (`this`.hasNext && !finished) {
59+
var nextEvaluated = false
60+
val elem = `this`.next()
61+
def getNext = { nextEvaluated = true; elem }
62+
val acc = op(result, getNext)
63+
finished = !nextEvaluated && acc == result
64+
result = acc
65+
}
66+
result
67+
}
68+
5569
def lazyFoldRight[B](z: B)(op: A => Either[B, B => B]): B = {
5670

5771
def chainEval(x: B, fs: immutable.List[B => B]): B =

src/test/scala/scala/collection/decorators/IterableDecoratorTest.scala

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,24 @@ class IterableDecoratorTest {
1717
Assert.assertEquals(10, List[Int]().foldSomeLeft(10)((x, y) => Some(x + y)))
1818
}
1919

20+
@Test
21+
def lazyFoldLeftIsStackSafe(): Unit = {
22+
val bigList = List.range(1, 50000)
23+
def sum(as: Iterable[Int]): Int =
24+
as.lazyFoldLeft(0)(_ + _)
25+
26+
Assert.assertEquals(sum(bigList), 1249975000)
27+
}
28+
29+
@Test
30+
def lazyFoldLeftIsLazy(): Unit = {
31+
val nats = LazyList.from(0)
32+
def exists[A](as: Iterable[A])(f: A => Boolean): Boolean =
33+
as.lazyFoldLeft(false)(_ || f(_))
34+
35+
Assert.assertTrue(exists(nats)(_ > 100000))
36+
}
37+
2038
@Test def lazyFoldRightIsLazy(): Unit = {
2139
val xs = LazyList.from(0)
2240
def chooseOne(x: Int): Either[Int, Int => Int]= if (x < (1 << 16)) Right(identity) else Left(x)

0 commit comments

Comments
 (0)