连接两个流时,`++` 和 `#:::` 在行为上的确切区别是什么?

What is the exact difference in behavior between `++` and `#:::` when you concatenate two streams?

更具体地说,我有一个类似于下面的代码(你们中的一些人可能会认出 coursera 函数式编程课程的最后一个作业的模式),但未能找到 [=12] 的解决方案=] 但与 #::: 一起工作(alreadySeen 解决方案集包含一个尚未探索的节点,并阻止求解器到达目标位置)。我伪装了代码以遵守协议,在此处的简短示例中 ++ 导致 WhosebugError 而 #::: 产生了好的解决方案。 ++ 的 scala 文档警告与其他集合一起使用时潜在的无限实现,但在我的示例中它在两个流之间使用。

case class PuzzleNode(pos: Position, moves: List[Move])

def reachableNodes(node: PuzzleNode): Stream[PuzzleNode] = ...

def from(initial: Stream[PuzzleNode], alreadySeen: Set[Position]): Stream[PuzzleNode] = {
  if (initial.isEmpty) Stream.empty
  else {
    val next = for {
      startNode <- initial
      node <- reachableNodes(startNode)
      if(!alreadySeen.contains(node.pos))
    } yield node

    //initial #::: from(next, alreadySeen ++ next.map(_.pos))
    initial ++ from(next, alreadySeen ++ next.map(_.pos))
  }
}

编辑: ++ 对以下代码表现出惰性行为,考虑到流 class 中的 ++ 实现,我可以理解它使用 cons 来构建结果流。

val fibs: Stream[BigInt] = BigInt(0) #:: BigInt(1) #:: fibs.zip(fibs.tail).map { n => n._1 + n._2 }
val prefix: Stream[BigInt]  = BigInt(4) #:: BigInt(8) #:: Stream.Empty
(prefix ++ fibs) take 10 foreach println

为什么上述 from 函数的行为不同?

++ 立即评估其参数(按值调用),强制对 from 的递归调用进行评估,从而导致 Whosebug#::: 延迟评估参数(按名称调用),因此 from 递归调用将在流被强制时发生。

您的编辑 ++ 没有表现出惰性行为,您使用已经定义的 Stream fibs 调用它,它只计算头部。它没有溢出的事实是因为尾部被懒惰地评估了。

要查看差异,请尝试以下操作:

 val prefix: Stream[BigInt]  = BigInt(4) #:: BigInt(8) #:: Stream.Empty
 val ss1 = prefix ++ {println("hello"); BigInt(4)} #:: BigInt(8) #:: Stream.Empty //will print 'hello' since the parameter to ++ is evaluated right away
 val ss2 = prefix #::: {println("hello"); BigInt(4)} #:: BigInt(8) #:: Stream.Empty //prints nothing, will print 'hello' when you reach the 3 component of the Stream
 ss2(1) //8, nothing printed
 ss2(2) //prints 'hello', 4
 ss2(2) //nothing printed since the Stream memoizes the value, 4