Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does my takeWhile fail to work with my Stream

Tags:

scala

The following code prints "*1". What's mystifying is if I remove the comment it returns "*4" which is what I was expecting

var max = 0
lazy val list: Stream[Int] = 1 #:: Stream.from(2)
list.takeWhile {
  x =>
    max = x
    x < 4
}//.foreach(println)
println("*" + max)
like image 523
deltanovember Avatar asked Oct 28 '11 00:10

deltanovember


1 Answers

First of all: the lazy in your second line isn't doing anything—you can remove it and get the same result.

More importantly: takeWhile is actually lazy, in that it just returns another Stream, and nothing past the head of that stream will be evaluated until it's needed. Consider the following:

val s = Stream.from(1).takeWhile(_ > 0)

You and I know that s is going to be an infinite stream, but if we fire up the REPL and type this in, it's perfectly happy to evaluate it:

scala> val s = Stream.from(1).takeWhile(_ > 0)
s: scala.collection.immutable.Stream[Int] = Stream(1, ?)

The same thing is happening in your example: the (Int) ⇒ Boolean that you've passed to takeWhile isn't going to get fed any elements beyond the head of the stream, until something like your foreach makes that necessary.

You can see this even more dramatically by adding something like a println inside of the takeWhile predicate:

scala> val s = Stream.from(1).takeWhile { x => println("Checking: " + x); x < 4 }
Checking: 1
s: scala.collection.immutable.Stream[Int] = Stream(1, ?)

scala> val l = s.toList
Checking: 2
Checking: 3
Checking: 4
l: List[Int] = List(1, 2, 3)

Clearly the predicate only gets called for the head of the stream, until we force the evaluation of the rest of the stream by calling toList.

like image 144
Travis Brown Avatar answered Oct 21 '22 11:10

Travis Brown