簡體   English   中英

將Haskell反轉計數器移植到Scala

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM