Algorithm 将Haskell反转计数器移植到Scala
下面是Haskell实现的一个例程的相关代码,该例程对列表中的反转进行计数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) =
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