Algorithm 将Haskell反转计数器移植到Scala

Algorithm 将Haskell反转计数器移植到Scala,algorithm,scala,sorting,haskell,Algorithm,Scala,Sorting,Haskell,下面是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) =

下面是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)
mergeAndCount::Ord a=>[a]->[a]->(Int[a])
合并与计数l@(x:xs)r@(y:ys)| x
我曾尝试在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)
      }
    }
  }
def mergeAndCount(l:Stream[Int],r:Stream[Int]):(Long,Stream[Int])={
(左、右)匹配{
大小写(x#:::xs,空)=>(0,l)
大小写(空,y#:::ys)=>(0,r)
格(x#:::xs,y#:::ys)=>if(x

关于如何使Scala版本表现得像Haskell版本有什么想法吗

我会将此作为评论,但不幸的是,我还没有这样做的声誉


无论如何,如果在函数的最后一次调用之前递归,则可能会遇到堆栈溢出错误-在Scala中,只有尾部递归被优化为不使用堆栈。如果您可以将递归调用移到每种情况的最后一行(这意味着放弃懒惰),那么您可能会得到更好的结果。

我会将此作为评论,但不幸的是,我还没有这样做的声誉


无论如何,如果在函数的最后一次调用之前递归,则可能会遇到堆栈溢出错误-在Scala中,只有尾部递归被优化为不使用堆栈。如果您可以将递归调用移到每种情况的最后一行(这意味着放弃惰性),那么您可能会得到更好的结果。

在这种情况下(将递归调用转换为尾部调用可能比较复杂),您可以使用以下方法以堆换堆栈:


如果我们不破坏尾部调用,这几乎肯定会破坏堆栈。

在这种情况下(将递归调用转换为尾部调用可能会很复杂),您可以使用以下方法以堆栈交换堆栈:


如果我们没有破坏尾部调用,这几乎肯定会毁掉堆栈。

如果我读对了,那么
懒惰的
修饰符会加深堆栈(但只会增加一个常数),而且我不确定他们是否在做你认为他们在做的事情。我手头没有scala,但我认为,你们两人的不懒惰破坏了声明
lazy val
的收益。你需要的是
mergeAndCount(…):(=>Long,Stream[Int])
之类的东西。@SassaNF我曾经尝试过使用
类懒散元组[a,b](x:=>a,y:=>b)
,但是如果我读得正确,它没有帮助,那么
lazy
修饰符会加深你的堆栈(但只会增加一个常数因子),而且我不确定他们是否在做你认为他们在做的事情。我手头没有scala,但我认为你的两人不懒惰会破坏声明
lazy val
的好处。您需要的是
mergeAndCount(…):(=>Long,Stream[Int])
之类的东西。@SassaNF我尝试过使用
类LazyTuple[a,b](x:=>a,y:=>b)
,但没有帮助,排序例程工作得很好,事实上它是懒惰的——获取结果流的第一个元素比获取最后一个要快得多。据我所知,
Tuple
constructor强制对其参数进行求值,从而导致堆栈溢出。事实上,排序例程工作得很好,而且实际上是懒惰的-获取结果流的第一个元素比获取最后一个元素快得多。据我所知,
Tuple
constructor强制对其参数进行求值,从而导致堆栈溢出。
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 #:: _))
  )
}
mergeAndCount((1 to 20000).toStream, (2 to 20001).toStream).run