diff options
author | Jason Zaugg <jzaugg@gmail.com> | 2012-09-29 18:15:15 +0200 |
---|---|---|
committer | Jason Zaugg <jzaugg@gmail.com> | 2012-11-04 21:21:51 +0100 |
commit | bc3dda2b0222d3b7cf3db491728b98f9b6110856 (patch) | |
tree | d71d81b7c443e75e12da82e9d6e8c53c84d31f1d /src/library/scala/collection/immutable/Stream.scala | |
parent | 2c6777fd53b93b95a261aadc87a5cbc03c14d503 (diff) | |
download | scala-bc3dda2b0222d3b7cf3db491728b98f9b6110856.tar.gz scala-bc3dda2b0222d3b7cf3db491728b98f9b6110856.tar.bz2 scala-bc3dda2b0222d3b7cf3db491728b98f9b6110856.zip |
SI-6448 Collecting the spoils of PartialFun#runWith
Avoids calling both `isDefinedAt` and `apply`. This pathological
case that would benefit the most looks like:
xs collect {
case x if {expensive(); true} => x
}
The typical change looks like:
- for (x <- this) if (pf.isDefinedAt(x)) b += pf(x)
+ foreach(pf.runWith(b += _))
Incorporates feedback provided by Pavel Pavlov:
https://github.com/retronym/scala/commit/ef5430
A few more opportunities for optimization are noted in the
`Pending` section of the enclosed test. `Iterator.collect`
would be nice, but a solution eludes me.
Calling the guard less frequently does change the behaviour
of these functions in an obervable way, but not contravene
the documented semantics. That said, there is an alternative
opinion on the comment of the ticket:
https://issues.scala-lang.org/browse/SI-6448
Diffstat (limited to 'src/library/scala/collection/immutable/Stream.scala')
-rw-r--r-- | src/library/scala/collection/immutable/Stream.scala | 13 |
1 files changed, 9 insertions, 4 deletions
diff --git a/src/library/scala/collection/immutable/Stream.scala b/src/library/scala/collection/immutable/Stream.scala index 5566806c55..e6b110131d 100644 --- a/src/library/scala/collection/immutable/Stream.scala +++ b/src/library/scala/collection/immutable/Stream.scala @@ -385,12 +385,17 @@ self => // 1) stackoverflows (could be achieved with tailrec, too) // 2) out of memory errors for big streams (`this` reference can be eliminated from the stack) var rest: Stream[A] = this - while (rest.nonEmpty && !pf.isDefinedAt(rest.head)) rest = rest.tail + + // Avoids calling both `pf.isDefined` and `pf.apply`. + var newHead: B = null.asInstanceOf[B] + val runWith = pf.runWith((b: B) => newHead = b) + + while (rest.nonEmpty && !runWith(rest.head)) rest = rest.tail // without the call to the companion object, a thunk is created for the tail of the new stream, // and the closure of the thunk will reference `this` if (rest.isEmpty) Stream.Empty.asInstanceOf[That] - else Stream.collectedTail(rest, pf, bf).asInstanceOf[That] + else Stream.collectedTail(newHead, rest, pf, bf).asInstanceOf[That] } } @@ -1170,8 +1175,8 @@ object Stream extends SeqFactory[Stream] { cons(stream.head, stream.tail filter p) } - private[immutable] def collectedTail[A, B, That](stream: Stream[A], pf: PartialFunction[A, B], bf: CanBuildFrom[Stream[A], B, That]) = { - cons(pf(stream.head), stream.tail.collect(pf)(bf).asInstanceOf[Stream[B]]) + private[immutable] def collectedTail[A, B, That](head: B, stream: Stream[A], pf: PartialFunction[A, B], bf: CanBuildFrom[Stream[A], B, That]) = { + cons(head, stream.tail.collect(pf)(bf).asInstanceOf[Stream[B]]) } } |