我正在浏览这本书"Functional Programming in Scala"并遇到了一个我不完全理解的例子。
在关于严格/懒惰的章节中,作者描述了Streams的构造,并且代码如下:
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)
}
...
}
我遇到的问题是智能构造函数(cons
),它调用Cons
案例类的构造函数。用于传递head
和tail
val的特定语法对我来说没有意义。为什么不像这样调用构造函数:
Cons(head, tail)
据我了解使用的语法,它强制创建两个只返回head
和tail
val的Function0对象。这与仅传递head
和tail
(没有() =>
前缀)有何不同,因为Cons
案例类已被定义为依次采用这些参数?这不是多余的吗?或者我错过了什么?
答案 0 :(得分:9)
首先,您假设=> A
和() => A
是相同的。但是,他们不是。例如,=> A
只能在按名称传递参数的上下文中使用 - 不可能声明类型为val
的{{1}}。由于=> A
参数始终为case class
s(除非明确声明为val
s),否则很清楚为什么var
无效。
其次,只是将带有参数的参数包装到带有空参数列表的函数中与上面的代码完成的不同:使用case class Cons[+A](h: => A, t: => Stream[A])
,确保lazy val
和hd
最多只评估一次。如果代码读了
tl
每次调用Cons(() => hd, () => tl)
对象的hd
方法(字段)时,将评估原始h
。使用Cons
,仅在第一次调用此lazy val
对象的hd
方法时评估h
,并且在每次后续调用中都返回相同的值。
在REPL中展示精简方式的差异:
Cons
注意第二次调用> def foo = { println("evaluating foo"); "foo" }
> val direct : () => String = () => foo
> direct()
evaluating foo
res6: String = foo
> direct()
evaluating foo
res7: String = foo
> val lzy : () => String = { lazy val v = foo; () => v }
> lzy()
evaluating foo
res8: String = foo
> lzy()
res9: String = foo
中的“evaluate foo”输出如何消失,而不是第二次调用lzy()
。
答案 1 :(得分:8)
区别在于=> A
不等于() => A
。
前者按名称传递,后者是不带参数(单位)并返回A的函数。
您可以在Scala REPL中对此进行测试。
scala> def test(x: => Int): () => Int = x
<console>:9: error: type mismatch;
found : Int
required: () => Int
def test(x: => Int): () => Int = x
^
在我的示例中简单引用x
会导致调用参数。在您的示例中,它正在构建一个延迟调用x的方法。
答案 2 :(得分:1)
请注意,方法cons
的参数是按名称参数(hd
和tl
)。这意味着如果您调用cons
,则在致电cons
之前不会评估参数;在cons
内使用它们时,它们将在稍后进行评估。
请注意,Cons
构造函数采用类型为Unit => A
的两个函数,但不是作为按名称参数。所以在调用构造函数之前会对它们进行评估。
如果您执行Cons(head, tail)
,则会评估head
和tail
,这意味着将评估hd
和tl
。
但这里的重点是避免在必要时(当有人访问hd
对象中的tl
或h
时)调用t
和Cons
。因此,您将两个匿名函数传递给Cons
构造函数;在有人访问h
或t
之前,不会调用这些函数。
答案 3 :(得分:0)
在def cons[A](hd: => A, tl: => Stream[A]) : Stream[A]
hd
的类型是A
,tl
是Stream[A]
而在case class Cons[+A](h: () => A, t: () => Stream[A]) extends Stream[A]
h的类型为Function0[A]
,t的类型为Function0[Stream[A]]
鉴于hd
的类型为A
,智能构造函数将案例类调用为
lazy val head = hd
lazy val tail = tl
Cons(() => head, () => tail) //it creates a function closure so that head is accessible within Cons for lazy evaluation