我们正在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(这里的问题是多么合适!)。 我期待它是尾部优化的。怎么了?
答案 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语句可能有所帮助。