Scala何时强制流值



我对流很满意,但我承认我对这种行为感到困惑:

import collection.immutable.Stream
object StreamForceTest extends App {
  println("Computing fibs")
  val fibs: Stream[BigInt] = BigInt(0) #:: BigInt(1) #::
     fibs.zip(fibs.tail).map((x: (BigInt, BigInt)) => {
       println("Adding " + x._1 + " and " + x._2);
       x._1 + x._2
     })
  println("Taking first 5 elements")
  val fibs5 = fibs.take(5)
  println("Computing length of that prefix")
  println("fibs5.length = " + fibs5.length)
}

带输出

Computing fibs
Taking first 5 elements
Computing length of that prefix
Adding 0 and 1
Adding 1 and 1
Adding 1 and 2
fibs5.length = 5

为什么take(5)而不是强制计算流的值,而length是否这样做?实际上谁都不需要看看这些值,但我会认为take更比CCD_ 4更有可能做到这一点。检查github上的源代码,我们发现take的这些定义(包括评论):

override def take(n: Int): Stream[A] = (
  // Note that the n == 1 condition appears redundant but is not.
  // It prevents "tail" from being referenced (and its head being evaluated)
  // when obtaining the last element of the result. Such are the challenges
  // of working with a lazy-but-not-really sequence.
  if (n <= 0 || isEmpty) Stream.empty
  else if (n == 1) cons(head, Stream.empty)
  else cons(head, tail take n-1)
)

length:

override def length: Int = {
  var len = 0
  var left = this
  while (!left.isEmpty) {
    len += 1
    left = left.tail
  }
  len
}

headtail的定义是从子类(EmptyCons)。(当然,Empty是一个对象,而不是类及其对headtail的定义例外情况。)有一些微妙之处,但它们似乎与制作有关确保CCD_ 15的CCD_;head这个定义直接来自关于Scala构造函数的第0讲。注意,length不会在head附近,但它是进行强制。

所有这些都是关于Scala流的接近程度的普遍困惑的一部分是Haskell列表。我以为哈斯克尔治疗了头部和尾部对称(我不是一个严肃的Haskell黑客),Scala强迫在更多情况下进行头部评估。我在想办法正是这些情况。

Stream的头是严格的,尾是懒惰的,正如您在cons.apply和cons构造函数中看到的那样:

def apply[A](hd: A, tl: => Stream[A]) = new Cons(hd, tl)
class Cons[+A](hd: A, tl: => Stream[A]) extends Stream[A]

请注意take方法引用tail:的上下文

cons(head, tail take n-1)

因为表达式tail take n-1被用作cons的第二个参数,该参数是按名称传递的,所以它不会强制计算tail take n-1,因此也不会强制计算tail

而在length中,语句

left = left.tail

,通过将left.tail分配给var,确实强制其评估。

Scala是"默认情况下严格的"。在大多数情况下,将对您引用的所有内容进行评估。只有在方法/构造函数参数使用=>声明按名称调用参数的情况下,我们才会进行延迟求值,在区域性中,除非有特殊原因,否则我们通常不会使用此方法。

让我提供另一个答案,这个答案只是从高层来看,即没有实际考虑代码。

如果你想知道一个流有多长,必须一直评估到最后。否则,你只能猜测它的长度。诚然,你可能并不真正关心价值观(因为你只想计算它们),但这无关紧要。

另一方面,当你从流(或者任何集合)中"获取"一定数量的元素时,你只是说你最多想要这个数量的元素。结果仍然是一个流,即使它可能已经被截断。

相关内容

  • 没有找到相关文章

最新更新