Scala lazy val explanation
I am using functional programming in a Scala course on Coursera and I am having a hard time understanding this piece of code -
def sqrtStream(x: Double): Stream[Double] = {
def improve(guess: Double): Double = (guess+ x/ guess) / 2
lazy val guesses: Stream[Double] = 1 #:: (guesses map improve)
guesses
}
This method will find 10 approximate square roots of 4 in ascending order of precision when I do sqrtSteam (4). Take (10). toList.
Can someone explain the guesswork estimation strategy here? My doubt is, what is the guessing value in the substitution when the second guessing value is picked up?
source to share
Let's start with a simplified example:
scala> lazy val a: Int = a + 5
a: Int = <lazy>
scala> a
Qaru here, because of infinite recursion
So, it is a
recalculated until it gets a stable value, for example here:
scala> def f(f:() => Any) = 0 //takes function with captured a - returns constant 0
f: (f: () => Any)Int
scala> lazy val a: Int = f(() => a) + 5
a: Int = <lazy>
scala> a
res4: Int = 5 // 0 + 5
You can replace def f(f:() => Any) = 0
on def f(f: => Any) = 0
, so the definition a
would look as if it really passed in f: lazy val a: Int = f(a) + 5
.
Streams use the same mechanism - guesses map improve
will be passed as a parameter called by name (and the lambda associated with the lazy a
will be stored inside the Stream, but not calculated until the tail is requested), so it likes lazy val guesses = #::(1, () => guesses map improve)
When you call guessess.head
- the tail will not be judged; guesses.tail
will return lazily Stream (improve(1), ?)
, guesses.tail.tail
will Stream(improve(improve(1)), ?)
, etc.
source to share
You can easily find out what's going on by changing the map function, as described in the scaladoc example :
scala> def sqrtStream(x: Double): Stream[Double] = {
| def improve(guess: Double): Double = (guess + x / guess) / 2
| lazy val guesses: Stream[Double] = 1 #:: (guesses map {n =>
| println(n, improve(n))
| improve(n)
| })
| guesses
| }
sqrtStream: (x: Double)Stream[Double]
Output:
scala> sqrtStream(4).take(10).toList
(1.0,2.5)
(2.5,2.05)
(2.05,2.000609756097561)
(2.000609756097561,2.0000000929222947)
(2.0000000929222947,2.000000000000002)
(2.000000000000002,2.0)
(2.0,2.0)
(2.0,2.0)
(2.0,2.0)
res0: List[Double] = List(1.0, 2.5, 2.05, 2.000609756097561, 2.0000000929222947, 2.000000000000002, 2.0, 2.0, 2.0, 2.0)
source to share
The value is guesses
not replaced. A stream is like a list, but its items are only evaluated when they are needed, and then they are stored, so the next time you receive them, the evaluation will not be needed. The link to the stream itself does not change.
At the top of the example Αλεχει it is written, there is a good explanation in the Scala API: http://www.scala-lang.org/api/current/index.html#scala.collection.immutable.Stream
source to share