我按照一本书的示例,在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
。这是因为对apply(as.head, ...)
的递归调用会对每个头进行评估吗?apply
以使其不进行强制评估?答案 0 :(得分:1)
没有。如果在println
上放置断点,则会在第一次创建Stream
时发现该方法实际被调用。行Stream(printAndReturn, ...
实际上会调用您的方法,但很多时候您将它放在那里。为什么?考虑cons
和apply
的类型签名:
def cons[A](hd: => A, tl: => Stream[A]): Stream[A]
VS
def apply[A](as: A*): Stream[A]
请注意,cons
的定义的参数标记为=> A
。这是一个名字参数。声明这样的输入使其变得懒惰,延迟其评估直到实际使用。因此,永远不会使用println
调用cons
。将其与apply
进行比较。您没有使用by name参数,因此传入该方法的任何内容都将自动获得评估。
不幸的是,到目前为止还没有一种超级简单的方法。你真正想要的是def apply[A](as: (=>A)*): Stream[A]
,但不幸的是Scala不支持通过名称参数的vararg。有关如何解决此问题的一些想法,请参阅this answer。一种方法是在创建Stream时包装函数调用:
Stream(
() => printAndReturn,
() => printAndReturn,
() => printAndReturn,
() => printAndReturn)
这会延迟评估。
答案 1 :(得分:0)
致电时
rds-combined-ca-bundle.pem
伴随对象中的apply被调用。查看应用的参数类型,您会发现它很严格。因此,在将参数分配给 as 之前,将首先对其进行评估。 原样变为一个整数数组
对于2,您可以将Apply定义为
ss1: {
ss1rootcert: fs.readFileSync("rds-combined-ca-bundle.pem").toString(), // wrong path
sslmode: "verify-full"
}
就像上面建议的那样,您需要像
一样将参数传递为thunk本身TIME