scala中mergesort的StackOverflower错误
我已经实现了下面的mergesort代码。但当整数数高达100000时,我在算法的合并过程中得到了StackOverflowerError。我在合并过程中使用带递归的模式匹配。我理解在合并过程中使用递归不是最佳的,因为这个输入的深度将高达50000。但是由于我使用的是scala,我希望通过一些编译器优化使递归调用迭代,因为这些都是尾部递归调用。你能帮我理解为什么我在下面的代码中仍然得到StackOverflowerError吗?请提供有关如何在scala中更有效地编写此内容的信息? 代码如下:scala中mergesort的StackOverflower错误,scala,recursion,scala-2.10,Scala,Recursion,Scala 2.10,我已经实现了下面的mergesort代码。但当整数数高达100000时,我在算法的合并过程中得到了StackOverflowerError。我在合并过程中使用带递归的模式匹配。我理解在合并过程中使用递归不是最佳的,因为这个输入的深度将高达50000。但是由于我使用的是scala,我希望通过一些编译器优化使递归调用迭代,因为这些都是尾部递归调用。你能帮我理解为什么我在下面的代码中仍然得到StackOverflowerError吗?请提供有关如何在scala中更有效地编写此内容的信息? 代码如下:
package common
object Merge {
def main(args: Array[String]) = {
val source = scala.io.Source.fromFile("IntegerArray.txt")
val data = source.getLines.map {line => line.toInt}.toList
println(data.length)
val res = mergeSort(data)
println(res)
}
def mergeSort(data: List[Int]): List[Int] = {
if(data.length <= 1) {data }
else {
val mid = (data.length)/2
val (l, r) = data.splitAt(mid)
val l1 = mergeSort(l)
val l2 = mergeSort(r)
merge(l1, l2)
}
}
def merge(l: List[Int], r: List[Int]): List[Int] = {
l match {
case List() => r
case x::xs => {
r match {
case List() => l
case y::ys => {
if(x<y) {
x :: merge(xs, r)
} else {
y :: merge(l, ys)
}
}
}
}
}
}
}
合并排序需要是递归的,但这不是问题,因为它是O(logn)。
merge
方法应优化为O(n)循环
TailRec优化仅在递归调用是最后一个命令时有效,在您的情况下,最后一个命令是列表串联(或前置)
您可以添加@tailrec
注释。编译器将始终尝试进行优化,但通过这种方式,它将让您知道它是否可以进行优化
merge(l1, l2, Nil)
...
@tailrec
def merge(l: List[Int], r: List[Int], acc: List[Int]): List[Int] = {
l match {
case List() => acc ::: r
case x::xs => {
r match {
case List() => acc ::: l
case y::ys => {
val (item, lTail, rTail) =
if(x<y) (x, xs, r)
else (y, l, ys)
merge(lTail, rTail, acc:::List(item))
}
}
}
}
}
合并(l1、l2、Nil)
...
@泰勒克
def合并(l:List[Int],r:List[Int],acc:List[Int]):List[Int]={
我匹配{
案例列表()=>acc:::r
案例x::xs=>{
r匹配{
案例列表()=>acc:::l
案例y::ys=>{
val(项目、lTail、rTail)=
if(xMerge排序需要递归,但这不是问题,因为它是O(logn)。Merge
方法应该优化为一个循环,因为它是O(n)
TailRec优化仅在递归调用是最后一个命令时有效,在您的情况下,最后一个命令是列表串联(或前置)
您可以添加@tailrec
注释。编译器将始终尝试进行优化,但通过这种方式,如果无法进行优化,它将通知您
merge(l1, l2, Nil)
...
@tailrec
def merge(l: List[Int], r: List[Int], acc: List[Int]): List[Int] = {
l match {
case List() => acc ::: r
case x::xs => {
r match {
case List() => acc ::: l
case y::ys => {
val (item, lTail, rTail) =
if(x<y) (x, xs, r)
else (y, l, ys)
merge(lTail, rTail, acc:::List(item))
}
}
}
}
}
合并(l1、l2、Nil)
...
@泰勒克
def合并(l:List[Int],r:List[Int],acc:List[Int]):List[Int]={
我匹配{
案例列表()=>acc:::r
案例x::xs=>{
r匹配{
案例列表()=>acc:::l
案例y::ys=>{
val(项目、lTail、rTail)=
如果(x也可以匹配元组:
def merge(l: List[Int], r: List[Int], acc: List[Int]): List[Int] = (l,r) match {
case (lh :: lt, rh :: rt) =>
if (lh < rh)
merge(lt, r, lh :: acc)
else
merge(l, rt, rh :: acc)
case _ => acc.reverse ::: l ::: r
}
def merge(l:List[Int],r:List[Int],acc:List[Int]):List[Int]=(l,r)匹配{
外壳(左侧:左侧,右侧:右侧)=>
如果(左侧<右侧)
合并(左、右、左::acc)
其他的
合并(左、右、右::acc)
大小写=>acc.reverse::l::r
}
如果按相反的顺序累积,则运行时间将不取决于实现的效率,您将得到您的O(n)
您还可以匹配元组:
def merge(l: List[Int], r: List[Int], acc: List[Int]): List[Int] = (l,r) match {
case (lh :: lt, rh :: rt) =>
if (lh < rh)
merge(lt, r, lh :: acc)
else
merge(l, rt, rh :: acc)
case _ => acc.reverse ::: l ::: r
}
def merge(l:List[Int],r:List[Int],acc:List[Int]):List[Int]=(l,r)匹配{
外壳(左侧:左侧,右侧:右侧)=>
如果(左侧<右侧)
合并(左、右、左::acc)
其他的
合并(左、右、右::acc)
大小写=>acc.reverse::l::r
}
如果您反向累积,您的运行时间将不取决于::
的实现效率,您将得到O(n)
呃,不,这些不是尾部递归调用。尾部调用是对::。应用
。请查看如何使尾部递归的最重要相关问题:如果您有“x::merge(xs,r)”在merge方法中,那么这不是尾部递归。尾部递归调用将是merge(A,b)在merge中,对merge的调用不是一个操作数对另一个操作数的调用。这不能优化为循环,它将保持递归,并可能破坏堆栈,这是您已经经历过的。呃,不,这些不是尾部递归调用。尾部调用是对::.apply
的调用。请查看顶部相关问题,了解o使其尾部递归:如果在merge方法中有“x::merge(xs,r)”,那么这不是尾部递归。尾部递归调用将是merge(A,b)在merge中,对merge的调用不是一个操作数对另一个操作数的调用。这不能优化为循环,它将保持递归,可能会破坏堆栈,这是您已经经历过的。这如何更有效?即使在这里它也取决于:::对吗?@PrathikPuthran这样,:
和reverse仅在基本情况下,如果:::
是O(n),你仍然得到O(n)合并而不是O(n*n)。这如何更有效?即使在这里它取决于:::对吗?@PrathikPuthran这样,:::
和反向只应用于基本情况,所以如果::
是O(n),你仍然得到O(n)合并而不是O(n*n)。
def merge(l: List[Int], r: List[Int], acc: List[Int]): List[Int] = (l,r) match {
case (lh :: lt, rh :: rt) =>
if (lh < rh)
merge(lt, r, lh :: acc)
else
merge(l, rt, rh :: acc)
case _ => acc.reverse ::: l ::: r
}