SICP说迭代过程(例如平方根计算的牛顿法,“pi”计算等)可以用Streams
表示。
是否有人在Scala中使用streams
来模拟迭代?
答案 0 :(得分:17)
这是生成pi近似值的一种方法:
val naturals = Stream.from(0) // 0, 1, 2, ...
val odds = naturals.map(_ * 2 + 1) // 1, 3, 5, ...
val oddInverses = odds.map(1.0d / _) // 1/1, 1/3, 1/5, ...
val alternations = Stream.iterate(1)(-_) // 1, -1, 1, ...
val products = (oddInverses zip alternations)
.map(ia => ia._1 * ia._2) // 1/1, -1/3, 1/5, ...
// Computes a stream representing the cumulative sum of another one
def sumUp(s : Stream[Double], acc : Double = 0.0d) : Stream[Double] =
Stream.cons(s.head + acc, sumUp(s.tail, s.head + acc))
val pi = sumUp(products).map(_ * 4.0) // Approximations of pi.
现在,假设您想要第200次迭代:
scala> pi(200)
resN: Double = 3.1465677471829556
...或300000:
scala> pi(300000)
resN : Double = 3.14159598691202
答案 1 :(得分:7)
当您执行一系列递归计算时,Streams非常有用,并且单个结果取决于之前的结果,例如计算pi。这是一个更简单的例子,考虑用于计算fibbonacci数的经典递归算法(1,2,3,5,8,13 ......):
def fib(n: Int) : Int = n match {
case 0 => 1
case 1 => 2
case _ => fib(n - 1) + fib(n - 2)
}
这段代码的一个要点是虽然非常简单,但效率极低。 fib(100)
几乎撞坏了我的电脑!每个递归分支为两个调用,并且您实际上是多次计算相同的值。
Streams允许您以递归方式进行动态编程,一旦计算了值,就会在每次需要时重复使用它。使用流实现上述内容:
val naturals: Stream[Int] = Stream.cons(0, naturals.map{_ + 1})
val fibs : Stream[Int] = naturals.map{
case 0 => 1
case 1 => 2
case n => fibs(n - 1) + fibs( n - 2)
}
fibs(1) //2
fibs(2) //3
fibs(3) //5
fibs(100) //1445263496
当递归解在O(2 ^ n)时间内运行时,Streams解决方案在O(n ^ 2)时间内运行。由于您只需要最后2个生成的成员,因此可以使用Stream.drop
轻松优化此成员,以便流大小不会溢出内存。