在其自己的定义中使用的变量?

A variable used in its own definition?

无限流:

val ones: Stream[Int] = Stream.cons(1, ones)

一个值怎么可能在它自己的声明中使用?看起来这应该会产生编译器错误,但它确实有效。

Stream.cons.apply的签名:

apply[A](hd: A, tl: ⇒ Stream[A]): Cons[A]

第二个参数的 表示它具有按名称调用的语义。因此,您的表达式 Stream.cons(1, ones) 没有得到严格评估;在作为 tl.

的参数传递之前,不需要计算参数 ones

并不总是递归定义。这实际上有效并产生 1:

val a : Int = a + 1
println(a)

变量a是在你键入val a: Int时创建的,所以你可以在定义中使用它。 Int 默认初始化为 0。 class 将为空。

正如@Chris 指出的那样,Stream 接受 => Stream[A] 因此应用了一些其他规则,但我想解释一般情况。想法还是一样的,但是变量是按名称传递的,所以这使得计算是递归的。由于它是按名称传递的,因此它会延迟执行。 Stream 逐个计算每个元素,因此每次需要下一个元素时它都会调用 ones,导致再次生成相同的元素。这有效:

val ones: Stream[Int] = Stream.cons(1, ones)
println((ones take 10).toList) // List(1, 1, 1, 1, 1, 1, 1, 1, 1, 1)

尽管您可以使无限流更容易:Stream.continually(1) 更新正如@SethTisue 在评论中指出的那样 Stream.continuallyStream.cons 是两种完全不同的方法,与非常不同的结果,因为 consAcontinually=>A,这意味着 continually 每次重新计算元素并将其存储在内存中,当cons可以避免存储它n次,除非你将它转换为其他结构,如List。仅当需要生成不同的值时才应使用 continually。有关详细信息和示例,请参阅@SethTisue 评论。

但请注意,您需要指定类型,与递归函数相同。

您可以使第一个示例递归:

lazy val b: Int = b + 1
println(b)

这会导致Whosebug。

这不会产生编译器错误的原因是因为 Stream.consCons 都是 non-strict and lazily evaluate 它们的第二个参数。

ones 可以在它自己的定义中使用,因为对象 cons 有一个应用方法定义如下:

/** A stream consisting of a given first element and remaining elements
 *  @param hd   The first element of the result stream
 *  @param tl   The remaining elements of the result stream
 */
def apply[A](hd: A, tl: => Stream[A]) = new Cons(hd, tl)

而缺点是这样定义的:

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

请注意,它的第二个参数 tl 是按名称 (=> Stream[A]) 而不是按值传递的。换句话说,参数 tl 在函数中使用之前不会计算。

使用此技术的一个优点是您可以编写可能仅部分计算的复杂表达式