Marcin Matuszak
Marcin Matuszak

Reputation: 1

Scala stream behaves counterintuitive

I am playing with Scala's streams and I'm not sure I catch the idea. Let's consider following code

def fun(s: Stream[Int]): Stream[Int] = Stream.cons(s.head, fun(s.tail))

executing this

val f = fun(Stream.from(7))
f take 14 foreach println

results with

7 8 9 10 ... up to 20

Let's say I understand this.

Now, changing slightly code (adding 2 to head)

def fun(s: Stream[Int]): Stream[Int] = Stream.cons(s.head + 2, fun(s.tail))

results in

9 10 11 ... up to 22

Again I think I understand. Problems starts with next example (d

def fun(s: Stream[Int]): Stream[Int] = Stream.cons(s.head / 2, fun(s.tail))

3 4 4 5 5 6 6 7 7 8 8 9 9 10

This I do not get, please explain why it results this way? Similar, subtracting also does not behave as I expect

def fun(s: Stream[Int]): Stream[Int] = Stream.cons(s.head - 2, fun(s.tail))

Output

5 6 7 8 9 10 ... up to 18

Upvotes: 0

Views: 131

Answers (3)

pagoda_5b
pagoda_5b

Reputation: 7373

Ok, let's try and break it down...

def fun(s: Stream[Int]): Stream[Int] = Stream.cons(s.head, fun(s.tail))

is a function that takes a Stream and separates its head and tail, applies itself recursively on the tail, and then recombines the two results with the cons operator.

Since the head is not touched during this operation, the Stream is rebuilt element by element as it was before.

val f = fun(Stream.from(7))

f it's the same as Stream.from(7) [i.e. an infinite sequence of increasing integers starting from 7]

Printing f take 14 in fact shows that we have the first 14 numbers starting from 7 [i.e. 7,8,9,...,20]

What happens next is that, while rebuilding the stream with the cons, each element is modified in some way

def fun(s: Stream[Int]): Stream[Int] = Stream.cons(s.head + 2, fun(s.tail))

This adds 2 to the head before recombining it with the modified tail. The latter is modified in the same way, its first element being added to 2 and then recombined to its own tail, and so own.

If we assume again that s contains the number from 7 on, what happens looks like

fun(s) = cons(7 + 2, cons(8 + 2, cons(9 + 2, ... ad infinitum ... )))))

This is the same as adding 2 to each and every element of the stream s.

The code confirms that by printing "9 to 22", which is exactly "7 to 20" with 2 added to every element.

The others examples are analogous:

  • the stream with each element divided by 2 (and rounded to the floor integer value, since the Stream is typed with Int values)
  • the stream where each element is decremented by 2

Upvotes: 0

Randall Schulz
Randall Schulz

Reputation: 26486

Is it more intuitive if you think of it as mapping the Stream?

scala> val s1 = Stream.from(10)
s1: scala.collection.immutable.Stream[Int] = Stream(10, ?)

scala> val s2 = s1 map (_ * 2)
s2: scala.collection.immutable.Stream[Int] = Stream(20, ?)

scala> s2.take(5).toList
res0: List[Int] = List(20, 22, 24, 26, 28)

scala> val s3 = s1 map (_ / 2)
s3: scala.collection.immutable.Stream[Int] = Stream(5, ?)

scala> s3.take(5).toList
res1: List[Int] = List(5, 5, 6, 6, 7)

scala> val s4 = s1 map (_ - 2)
s4: scala.collection.immutable.Stream[Int] = Stream(8, ?)

scala> s4.take(5).toList
res2: List[Int] = List(8, 9, 10, 11, 12)

Upvotes: 0

Richard Sitze
Richard Sitze

Reputation: 8463

Given your "take": 7 8 9 10 ... up to 20,

  • what happens when you + 2 on each element?

  • what happens when you / 2 on each element (int arithmetic)?

  • what happens when you - 2 on each element?

Upvotes: 1

Related Questions