在延迟流中,保留共享意味着什么?

Izb*_*gen 5 functional-programming scala

我正在关注《 Scala中的函数编程》一书。这是Stream定义和函数的代码片段,这些代码片段constant使用智能构造函数并使用来构造unfold

sealed trait Stream[+A]
case object Empty extends Stream[Nothing]
case class Cons[+A](h: () => A, tl: () => Stream[A]) extends Stream[A]

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

  def constant[A](a: A): Stream[A] = cons(a, constant(a))

  def unfold[A, S](z: S)(f: S => Option[(A, S)]): Stream[A] =
    f(z).fold(empty[A])(x => cons(x._1, unfold(x._2)(f)))

  def constantViaUnfold[A](a: A): Stream[A] = unfold(a)(x => Some((x, x)))
}
Run Code Online (Sandbox Code Playgroud)

有一个脚注说:

使用unfold定义constant意味着我们不会像递归定义那样获得共享。即使我们遍历遍历它时,递归定义也会消耗常量内存,而基于展开的实现则不会。在使用流进行编程时,保留共享并不是我们通常所依赖的东西,因为它非常微妙,并且不受类型跟踪。例如,即使呼叫偶数,共享也会被破坏xs.map(x => x).

作者在说我们没有分享时是什么意思?究竟共享了什么,为什么它没有保留在unfold版本中?同样,关于恒定内存消耗的句子也不清楚。

Krz*_*sik 5

假设您创建了一个这样的新列表:

val n0 = Nil       //List()
val n1 = 1 :: n0   //List(1)
val n2 = 2 :: n1   //List(2,1)
val n3 = 3 :: n2   //List(3,2,1)
Run Code Online (Sandbox Code Playgroud)

如果你建立一个这样的列表,可以很容易地通知,N3是真的N23前置和N2只是N12前置等,所以参考1共享N1N2N3和参考2被共享n2n2等。你也可以这样写:

Cons(3, Cons(2, Cons(1, Nil)))
Run Code Online (Sandbox Code Playgroud)

FPinS的示例中,当您递归创建Stream时,情况也是如此。You Stream是从嵌套的Cons构建的,每个子流都与其父流共享元素。因此,当创建下一个Stream时,它只是用新元素将旧的Stream包裹在Cons 中。但是由于Stream是惰性的,因此所有Cons层次结构的构建只有在您实现它时才会完成,例如通过调用toList

像这样构建流也会使您持续消耗内存,因为从前一个流创建下一个流只会为新元素消耗内存。

为什么它不是一个案例unfold?因为它以“其他方式”构建 Stream。所以它是这样的:

Cons(x, next_iteration_of_unfold)                    //1st iteration
Cons(x, Cons(x, next_iteration_of_unfold))           //2nd iteration
Cons(x, Cons(x, Cons(x, next_iteration_of_unfold)))  //3rd iteration, etc.
Run Code Online (Sandbox Code Playgroud)

所以正如你所看到的,没有什么可以共享的。

编辑:

您可以通过调用本书的take最终实现并添加toStringCons以下内容来查看物化流的外观:

override def toString: String = s"Cons(${h()}, ${tl()})"
Run Code Online (Sandbox Code Playgroud)

进而:

Stream.ones.take(3)
Run Code Online (Sandbox Code Playgroud)

你会看见:

Cons(1, Cons(1, Cons(1, Empty)))
Run Code Online (Sandbox Code Playgroud)