[英]Scala lazy evaluation and apply function
我正在按照一本书的示例在 Scala 中使用惰性求值来实现 Steam 类。
sealed trait Stream[+A]
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: _*))
}
}
然后我用一个简单的函数来测试它是否有效
def printAndReturn: Int = {
println("called")
1
}
然后我构造 Stream 如下:
println(s"apply: ${
Stream(
printAndReturn,
printAndReturn,
printAndReturn,
printAndReturn
)
}")
输出是这样的:
called
called
called
called
apply: Cons(fpinscala.datastructures.Stream$$$Lambda$7/1170794006@e580929,fpinscala.datastructures.Stream$$$Lambda$8/1289479439@4c203ea1)
然后我使用cons
构造了 Stream :
println(s"cons: ${
cons(
printAndReturn,
cons(
printAndReturn,
cons(printAndReturn, Empty)
)
)
}")
输出是:
cons: Cons(fpinscala.datastructures.Stream$$$Lambda$7/1170794006@2133c8f8,fpinscala.datastructures.Stream$$$Lambda$8/1289479439@43a25848)
所以这里有两个问题:
printAndReturn
所有printAndReturn
求值。 这是因为对apply(as.head, ...)
的递归调用会评估每个头部吗?apply
以使其不强制评估?不。如果您在println
上放置一个断点,您会发现该方法实际上是在您第一次创建Stream
时被调用的。 Stream(printAndReturn, ...
实际上会调用你的方法,不管你把它放在那里多少次。为什么?考虑cons
和apply
的类型签名:
def cons[A](hd: => A, tl: => Stream[A]): Stream[A]
对比:
def apply[A](as: A*): Stream[A]
请注意, cons
的定义将其参数标记为=> A
。 这是一个按名称参数。 像这样声明输入会使其变得懒惰,将其评估延迟到实际使用为止。 因此,您的println
永远不会被使用cons
调用。 比较这个apply
。 您没有使用按名称参数,因此任何传入该方法的内容都将自动进行评估。
不幸的是,目前还没有一种超级简单的方法。 你真正想要的是def apply[A](as: (=>A)*): Stream[A]
但不幸的是 Scala 不支持按名称参数的 vararg 。 有关如何解决此问题的一些想法,请参阅此答案。 一种方法是在创建流时包装函数调用:
Stream( () => printAndReturn, () => printAndReturn, () => printAndReturn, () => printAndReturn)
这将延迟评估。
当你打电话
Stream(
printAndReturn,
printAndReturn,
printAndReturn,
printAndReturn
)
调用了伴随对象中的应用程序。 查看 apply 的参数类型,您会注意到它是严格的。 因此,在分配给as之前,将首先评估参数。 什么是成为是int数组
对于 2,您可以将应用定义为
def apply[A](as: (() => A)*): Stream[A] =
if (as.isEmpty) empty else cons(as.head(), apply(as.tail: _*))
正如上面所建议的,您需要将参数作为 thunks 本身传递给
println(s"apply: ${Stream(
() => printAndReturn,
() => printAndReturn,
() => printAndReturn,
() => printAndReturn
)}")
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.