Scala 合并两个流(有序)以获得最终排序的流
例如,如何合并两个排序整数流?我认为这是非常基本的,但我发现这一点都不琐碎。下面的一个不是尾部递归的,当流较大时,它将堆栈溢出Scala 合并两个流(有序)以获得最终排序的流,scala,stream,lazy-evaluation,tail-recursion,Scala,Stream,Lazy Evaluation,Tail Recursion,例如,如何合并两个排序整数流?我认为这是非常基本的,但我发现这一点都不琐碎。下面的一个不是尾部递归的,当流较大时,它将堆栈溢出 def merge(as: Stream[Int], bs: Stream[Int]): Stream[Int] = { (as, bs) match { case (Stream.Empty, bss) => bss case (ass, Stream.Empty) => ass case (a #:: ass, b #:: b
def merge(as: Stream[Int], bs: Stream[Int]): Stream[Int] = {
(as, bs) match {
case (Stream.Empty, bss) => bss
case (ass, Stream.Empty) => ass
case (a #:: ass, b #:: bss) =>
if (a < b) a #:: merge(ass, bs)
else b #:: merge(as, bss)
}
}
def合并(as:Stream[Int],bs:Stream[Int]):Stream[Int]={
(as,bs)匹配{
案例(Stream.Empty,bss)=>bss
案例(ass,Stream.Empty)=>ass
案例(a::ass,b::bss)=>
如果(a
我们可能希望通过引入累加器将其转化为尾部递归。然而,如果我们预先挂起累加器,我们只会得到一个相反顺序的流;如果我们用连接(#::)附加累加器,它就不再是懒惰(严格的)
这里的解决方案是什么?谢谢将评论转换为答案,您的合并没有问题 它根本不是递归的——任何一个对merge的调用都会返回一个新流,而不需要任何其他对merge的调用
a#::merge(ass,bs)
返回包含第一个元素a
的流,其中merge(ass,bs)
将在需要时被调用以评估流的其余部分
所以
很好用。没有堆栈溢出。“下面的一个不是尾部递归的,当流较大时,它将堆栈溢出。”为什么?递归不是“真实的”,因为“递归”调用中的合并还不应该计算。也就是说,您的代码根本不是递归的,因为它从
if
的任一分支返回一个新流?还是我误解了?谢谢你的回答。问题是,因为它不是尾部递归的,所以上面的示例代码将发出OOM,因为我猜堆栈帧将包含对流头部的引用。我不确定这是否正确(但我没有测试它)。一旦访问流中的下一个元素,上一个堆栈帧/闭包将不被引用,并且是GC;'d、 我想。
val m = merge(Stream.from(1,2), Stream.from(2, 2))
//> m : Stream[Int] = Stream(1, ?)
m.drop(10000000).take(1)
//> res0: scala.collection.immutable.Stream[Int] = Stream(10000001, ?)