尾递归问题

时间:2011-10-19 13:58:41

标签: scala stack-overflow tail-recursion tail-call-optimization

我们正在Scala中尝试并行收集,并想检查结果是否已订购。为此,我在REPL上编写了一个小函数来检查我们正在生成的非常大的List:

def isOrdered(l:List[Int]):Boolean = { l match { 
  case Nil => true
  case x::Nil => true
  case x::y::Nil => x>y
  case x::y::tail => x>y & isOrdered(tail) 
  }
}

它失败了stackOverflow(这里的问题是多么合适!)。 我期待它是尾部优化的。怎么了?

4 个答案:

答案 0 :(得分:14)

isOrdered不是代码中的最后一个调用,&运营商是。试试这个:

@scala.annotation.tailrec def isOrdered(l:List[Int]):Boolean = { l match { 
  case Nil => true
  case x::Nil => true
  case x::y::Nil => x>y
  case x::y::tail => if (x>y) isOrdered(tail) else false
  }
}

答案 1 :(得分:8)

您的算法不正确。即使@Kim的改进,isOrdered(List(4,3,5,4))也会返回true

试试这个:

def isOrdered(l:List[Int]): Boolean = l match {
  case Nil => true
  case x :: Nil => true
  case x :: y :: t => if (x <= y) isOrdered(l.tail) else false
}

(也更新以使标志正确)

修改:我的推荐布局是:

def isOrdered(list: List[Int]): Boolean = list match {
  case Nil      => true
  case x :: Nil => true
  case x :: xs  => if (x > xs.head) false
                   else isOrdered(xs)
}

如果性能不是问题的快速方法将是

def isOrdered(l: List[Int]) = l == l.sorted

答案 2 :(得分:2)

它无法进行尾部优化,因为您将其返回:'x&gt; y&amp; isOrdered(尾部)”。这意味着它需要将它保留在堆栈中。

当您期望函数是尾递归时,使用@tailrec批注强制出错。它也将解释为什么它不可能。

答案 3 :(得分:1)

我认为问题在于你在最后一种情况下使用了bitwise和operator(&amp;)。由于运行时需要知道isOrdered调用的值才能评估&amp;,因此无法对函数进行尾部优化。 (也就是说,运行更多的代码 - 在调用isOrdered之后的按位和操作。)

使用&amp;&amp;或if语句可能有所帮助。