Aamir
Aamir

Reputation: 2424

Streams in Scala

I have a piece of code that is calculating some series:

object Problem7 extends App {
  lazy val ps: Stream[Int] = 2 #:: Stream.from(3).filter(i =>
  ps.takeWhile(j => j * j <= i).forall(i % _ > 0))

  val r = ps(10000)
  println(r)
}

hereps is of type collection Stream which is a variable actually but how its possible to call it like this ps(10000) as its not any method and I also have one more expression val fs:Stream[Int] = 0 #:: fs.scanLeft(1)(_ + _), here also I'm a little ambiguous about how we are calling fs.scanLeft(1)(_+_) in the variable itself.Someone help!!

Upvotes: 2

Views: 591

Answers (2)

Pranav Shukla
Pranav Shukla

Reputation: 2226

For the second part of the question,

lazy val fs:Stream[Int] = 0 #:: fs.scanLeft(1)(_ + _)

The #:: is cons operator Stream.cons used for constructing streams. The parameter of ConsWrapper is a by-name parameter and hence it is evaluated lazily.

It is therefore valid to reference fs after the #:: operator. I think we have to change the val to lazy val, otherwise the scala compiler throws forward reference extends over definition of value fs.

Upvotes: 1

Yuval Itzchakov
Yuval Itzchakov

Reputation: 149518

here ps is of type collection Stream which is a variable actually but how its possible to call it like this ps(10000) as its not any method

It's possible to call ps(10000) because Stream has an apply method (Inherited from LinerSeqOptimized):

/** Selects an element by its index in the $coll.
 *  Note: the execution of `apply` may take time proportial to the index value.
 *  @throws `IndexOutOfBoundsException` if `idx` does not satisfy `0 <= idx < length`.
 */
def apply(n: Int): A = {
  val rest = drop(n)
  if (n < 0 || rest.isEmpty) throw new IndexOutOfBoundsException("" + n)
  rest.head
}

Calling () on any object in Scala will have the compiler looking for an apply method on the object. This is similar to how you can instantiate case classes without the new keyword, as the compiler generates apply and unapply automatically for you.

If we'll look at a simpler reproduce:

def main(args: Array[String]): Unit = {
  val ps: Stream[Int] = Stream.from(1, 1)
  val r = ps(1)
  println(r)
}

The compiler is actually doing:

val r = ps.apply(1)

Where apply for a collection usually looks up the element at index i:

scala> val ps: Stream[Int] = Stream.from(1, 1)
ps: Stream[Int] = Stream(1, ?)

scala> val r = ps(1)
r: Int = 2

scala> val x = ps.apply(1)
x: Int = 2

If you actually want to see what the compiler generates:

object Problem7 extends Object {
    def main(args: Array[String]): Unit = {
      val ps: scala.collection.immutable.Stream = scala.`package`.Stream().from(1, 1);
      val r: Int = scala.Int.unbox(ps.apply(1));
      scala.this.Predef.println(scala.Int.box(r))
    };

    def <init>(): Problem7.type = {
      Problem7.super.<init>();
      ()
    }
  }
}

Upvotes: 3

Related Questions