懒惰的评估解释

时间:2015-10-20 20:34:06

标签: scala

更新:使用两个构造函数的用法更新了代码,基本上我感到困惑的是,ConsStream.cons之间有什么区别。附:这个例子来自Book Scala中的函数编程的第5章。

所以任何人都可以向我解释为什么下面的懒惰评估没有按预期工作?我的Scala工作表的输出是

One
res0: Int = 1
One
res1: Int = 2
One
res2: Int = 3
One
res3: Int = 4
One
res4: Int = 5
One
res5: Int = 6

清除这不是预期的输出,因为延迟评估One应该只打印一次而i只应增加一次,但似乎并非如此。我错过了一些东西,却看不到它,任何一双新鲜的眼睛都在伸出援助之手?

sealed trait Stream[+A] {
  def toList: List[A] = {
    @annotation.tailrec
    def go(s: Stream[A], acc: List[A]): List[A] = s match {
      case Cons(h,t) => go(t(), h() :: acc)
      case _ => acc
    }
    go(this, List()).reverse
  }
}
case object Empty extends Stream[Nothing]
case class Cons[+A](h: () => A, t: () => Stream[A]) extends Stream[A]

object Stream {
  def cons[A](hd: => A, tl: => Stream[A]): Stream[A] = {
    lazy val head = hd
    lazy val tail = tl
    Cons(() => head, () => tail)
  }

  def empty[A]: Stream[A] = Empty

  def apply[A](as: A*): Stream[A] =
    if (as.isEmpty) empty
    else cons(as.head, apply(as.tail: _*))
}

var i = 0
val nonLazy = Cons(
  () => { println("One"); i+=1; i }, 
  () => Cons(
    () => { println("Two"); i+=2; i }, 
    () => Empty))
nonLazy.h
nonLazy.h
nonLazy.h

var i = 0
val lazy = Stream.cons(
  () => { println("One"); i+=1; i }, 
  Stream.cons(
    () => { println("Two"); i+=2; i }, 
    Empty)).toList
lazy.head
lazy.head
lazy.head
lazy.head
lazy.head
lazy.head

3 个答案:

答案 0 :(得分:2)

lazy val head = hd

因为您没有指定head的类型,它将是() => Int,并且每当您获取头部时,hd正在评估,i增加并增加

我认为您要计算hd并将值存储在Stream.cons函数中,因此您需要明确指定类型:

lazy val head: A = hd

tail变量也是如此。

答案 1 :(得分:1)

我认为您忘记了hd是什么 - 即返回Int的function0,或使用简写() => Int

当你这样称呼时:

stream.head()

这是做什么的:

  1. 检索stream.head(这是一个函数,因此有一个apply()方法可以调用,它将打印'One',将i递增2,然后返回{{ 1}}。
  2. 执行该功能,因为i是Scala使其变得简单易用的方式。
  3. 如果您不想执行该功能,请尝试以下操作:

    ()

    这将返回:

    apply()

    stream.head stream.head stream.head stream.head stream.head stream.head 确实是懒惰的 - 每次都是相同的功能 - 但是你要执行它6次!

答案 2 :(得分:1)

如果您想致电Stream.cons(()=>{println("One"); i+=1; i}, Stream.cons(()=>{println("Two"); i+=2; i}, Empty)),则需要修改其类型:

def cons[A](hd: () => A, tl: () => Stream[A]): Stream[A] = {
  lazy val head = hd()
  lazy val tail = tl()
  Cons(() => head, () => tail)
}

要访问它,您需要

val stream = Stream.cons(()=>{println("One"); i+=1; i}, Stream.cons(()=>{println("Two"); i+=2; i}, Stream.Empty)) // won't print anything
stream.h() // will print "One" and return 1
stream.h() // will return 1 without printing anything
stream.h() // will return 1 without printing anything

使用toList会阻止您实际看到懒惰。