scala中mergesort的StackOverflower错误

scala中mergesort的StackOverflower错误,scala,recursion,scala-2.10,Scala,Recursion,Scala 2.10,我已经实现了下面的mergesort代码。但当整数数高达100000时,我在算法的合并过程中得到了StackOverflowerError。我在合并过程中使用带递归的模式匹配。我理解在合并过程中使用递归不是最佳的,因为这个输入的深度将高达50000。但是由于我使用的是scala,我希望通过一些编译器优化使递归调用迭代,因为这些都是尾部递归调用。你能帮我理解为什么我在下面的代码中仍然得到StackOverflowerError吗?请提供有关如何在scala中更有效地编写此内容的信息? 代码如下:

我已经实现了下面的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
}