我正在使用Coursera的Scala课程中的函数式编程,我很难理解这段代码片段 -
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
}
当我做sqrtSteam(4).take(10).toList时,这个方法会以递增的精度顺序找到10个近似的4的平方根。
有人可以在这里解释猜测的评估策略吗?我怀疑当猜测的第二个价值被提升时,猜测的价值取代了什么?
答案 0 :(得分:3)
让我们从简化的例子开始:
scala> lazy val a: Int = a + 5
a: Int = <lazy>
scala> a
stack overflow here, because of infinite recursion
所以a
重新计算,直到它得到一些稳定的值,就像这里:
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
您可以将def f(f:() => Any) = 0
替换为def f(f: => Any) = 0
,因此a
定义看起来确实会传递给f:lazy val a: Int = f(a) + 5
。
Streams使用相同的机制 - guesses map improve
将作为名称调用的参数传递(链接到惰性a
的lambda将保存在Stream中,但在请求tail之前不会计算),因此它& #39;类似于lazy val guesses = #::(1, () => guesses map improve)
。当你致电guessess.head
时 - 不会评估尾巴; guesses.tail
会懒洋洋地返回Stream (improve(1), ?)
,guesses.tail.tail
将会Stream(improve(improve(1)), ?)
,依此类推。
答案 1 :(得分:0)
您可以通过修改地图功能轻松找出正在发生的事情,如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]
输出结果为:
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)
答案 2 :(得分:0)
guesses
的值未被替换。流类似于列表,但仅在需要时才对其元素进行评估,然后将其存储,因此下次访问它们时,评估将不再必要。对流本身的引用不会改变。
在Αλεχει上写的示例中,Scala API中有一个很好的解释: http://www.scala-lang.org/api/current/index.html#scala.collection.immutable.Stream