4

I am trying to count inversions for a list of numbers. The following Frege program works for small set of numbers but throws StackOverflowError for 100000 numbers.

import frege.IO

inversionCount [] _ = (0, [])
inversionCount [x] _ = (0, [x])
inversionCount xs n = (count, sorted) where
  count = lcount + rcount + mergecount
  (lcount, lsorted) = inversionCount left lsize
  (rcount, rsorted) = inversionCount right rsize
  (mergecount, sorted) = inversionMergeCount lsorted lsize rsorted rsize (0, [])
  (left, right) = splitAt mid xs
  mid = n `quot` 2
  lsize = mid
  rsize = n - mid

inversionMergeCount xs _ [] _ (acc,sorted) = (acc, reverse sorted ++ xs)
inversionMergeCount [] _ ys _ (acc,sorted) = (acc, reverse sorted ++ ys)
inversionMergeCount (xs@(x:restx)) m (ys@(y:resty)) n (acc, sorted)
  | x < y = inversionMergeCount restx (m - 1) ys n (acc, x:sorted)
  | x > y = inversionMergeCount xs m resty (n - 1) (acc + m, y:sorted)
  | otherwise = inversionMergeCount restx (m - 1) resty (n - 1) (acc, x:y:sorted)

main (fileName:_) = do
  input <- readFile fileName
  let xs = map atoi (lines input)
  println . fst $ inversionCount xs 100000

--Haskell's readFile using Java's Scanner
type JScanner = JScannerT RealWorld

data JScannerT s = native java.util.Scanner where
  native new :: File -> ST s (Exception JScanner)
  native useDelimiter :: JScanner -> String -> ST s JScanner
  native next :: JScanner -> ST s String

readFile f = do
  file <- File.new f
  exceptionScanner <- JScanner.new file
  let scanner = either throw id exceptionScanner
  scanner.useDelimiter "\\Z"
  scanner.next

The same code in Haskell works fine:

import System.Environment

inversionCount [] _ = (0, [])
inversionCount [x] _ = (0, [x])
inversionCount xs n = (count, sorted) where
  count = lcount + rcount + mergecount
  (lcount, lsorted) = inversionCount left lsize
  (rcount, rsorted) = inversionCount right rsize
  (mergecount, sorted) = inversionMergeCount lsorted lsize rsorted rsize (0, [])
  (left, right) = splitAt mid xs
  mid = n `quot` 2
  lsize = mid
  rsize = n - mid

inversionMergeCount xs _ [] _ (acc,sorted) = (acc, reverse sorted ++ xs)
inversionMergeCount [] _ ys _ (acc,sorted) = (acc, reverse sorted ++ ys)
inversionMergeCount (xs@(x:restx)) m (ys@(y:resty)) n (acc, sorted)
  | x < y = inversionMergeCount restx (m - 1) ys n (acc, x:sorted)
  | x > y = inversionMergeCount xs m resty (n - 1) (acc + m, y:sorted)
  | otherwise = inversionMergeCount restx (m - 1) resty (n - 1) (acc, x:y:sorted)


main = do
  (fileName: _) <- getArgs
  contents <- readFile fileName
  let xs :: [Int]
      xs = map read (lines contents)
  print . fst $ inversionCount xs 100000

What could be the cause for stack overflow? Is it some function not being tail-recursive?

4

2 回答 2

5

Haskell 很可能有更好的严格性分析器,或者尾递归的实现方式不同,或者运行时只是有更多可用的堆栈。

我会尝试的第一件事是设置-Xss8m,甚至16m。

如果这没有帮助,请记住,使用 (-)、(+) 等严格函数的应用程序更新的惰性参数会构建有时稍后必须立即评估的 thunk。这与 with 的问题相同foldl,看起来 inversionMergeCount 和 inversionCount 的第二个参数受此影响。

如果 Frege 编译器看到这一点,它应该对此发出警告,但目前还没有。

另一点是,为什么要在元组中传递最后两个参数?您也可以使 acc 严格。

于 2013-02-19T23:23:42.890 回答
3

根据 Ingo 的建议,我进行了更改,代码现在可以正常工作。我还必须计数Integer而不是Int避免 int 溢出。这是带有严格注释和Integer转换的更新代码:

inversionCount [] _ = (zero, [])
inversionCount [x] _ = (zero, [x])
inversionCount xs n = (count, sorted) where
  count = lcount + rcount + mergecount
  (lcount, lsorted) = inversionCount left lsize
  (rcount, rsorted) = inversionCount right rsize
  (mergecount, sorted) = inversionMergeCount lsorted lsize rsorted rsize zero []
  (left, right) = splitAt mid xs
  mid = n `quot` 2
  lsize = mid
  rsize = n - mid


inversionMergeCount xs _ [] _ !acc sorted = (acc, reverse sorted ++ xs)
inversionMergeCount [] _ ys _ !acc sorted = (acc, reverse sorted ++ ys)
inversionMergeCount (xs@(x:restx)) !m (ys@(y:resty)) n !acc sorted
  | x < y = inversionMergeCount restx (pred m) ys n acc (x:sorted)
  | x > y = inversionMergeCount xs m resty (pred n) (acc + m.big) (y:sorted)
  | otherwise = inversionMergeCount restx (pred m) resty (pred n) acc (x:y:sorted)
于 2013-02-20T06:51:04.540 回答