[英]Porting Haskell inversion counter to Scala
这是Haskell实现例程的相关代码,该例程计算列表中的反转
mergeAndCount :: Ord a => [a] -> [a] -> (Int,[a])
mergeAndCount l@(x:xs) r@(y:ys) | x < y = let (inv, s) = mergeAndCount xs r in (inv, x:s)
| otherwise = let (inv, s) = mergeAndCount l ys in (inv + rest, y:s)
where rest = length l
mergeAndCount l [] = (0, l)
mergeAndCount [] r = (0, r)
我试图在Scala中编写类似的例程,但是由于堆栈溢出而崩溃(不过,延迟排序有效)。 这是无效版本:
def mergeAndCount(l: Stream[Int], r: Stream[Int]) : (Long, Stream[Int]) = {
(l, r) match {
case (x#::xs, Empty) => (0, l)
case (Empty, y#::ys) => (0, r)
case (x#::xs, y#::ys) => if(x < y) {
lazy val (i, s) = mergeAndCount(xs, r)
(i, x#::s)
} else {
lazy val (i, s) = mergeAndCount(l, ys)
(i + l.length, y#::s)
}
}
}
关于如何使Scala版本像Haskell那样表现的任何想法吗?
您可以使用蹦床在这种情况下(将递归调用转换为尾调用可能会很复杂),以堆换栈:
import Stream.Empty
import scalaz.std.tuple._
import scalaz.syntax.bifunctor._
import scalaz.Free.Trampoline, scalaz.Trampoline._
def mergeAndCount(
l: Stream[Int],
r: Stream[Int]
): Trampoline[(Long, Stream[Int])] = (l, r) match {
case (_ #:: _, Empty) => done((0, l))
case (Empty, _ #:: _) => done((0, r))
case (x #:: xs, y #:: _) if x < y => suspend(
mergeAndCount(xs, r).map(_.rightMap(x #:: _))
)
case (_, y #:: ys) => suspend(
mergeAndCount(l, ys).map(_.bimap(_ + l.length, y #:: _))
)
}
请注意,我在这里使用Scalaz的实现,因为标准库当前缺少一些内容(尽管很快就会改变 )。
现在,您可以编写例如以下内容:
mergeAndCount((1 to 20000).toStream, (2 to 20001).toStream).run
如果我们不踩尾巴电话,这几乎肯定会打击筹码。
我会发表评论,但不幸的是我还没有这样的声誉...
无论如何,如果在函数的最后一次调用之前进行递归操作,则很可能会遇到堆栈溢出错误-在Scala中,仅优化了尾递归而不使用堆栈。 如果您可以将递归调用移至每种情况的最后一行(这意味着放弃惰性),则可能会得到更好的结果。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.