[英]Count inversions: StackOverflowError in Frege, works fine in Haskell
我试图计算一个数字列表的倒数。 以下Frege程序适用于少量数字,但抛出StackOverflowError为100000个数字。
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
Haskell中的相同代码工作正常:
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
堆栈溢出的原因是什么? 这是一些函数不是尾递归的吗?
Haskell很可能有一个更好的严格性分析器,或者尾部递归的实现方式不同,或者运行时只有更多的堆栈可用。
我要尝试的第一件事是设置-Xss8m,甚至是16m。
如果这没有帮助,请记住,使用诸如( - ),(+)等严格函数的应用程序更新的延迟参数构建了有时候必须立即评估的thunk。 这与foldl
问题相同,看起来像inversionMergeCount和inversionCount的第二个参数受此影响。
如果它看到这个,Frege编译器应该警告这个,但它现在不是。
另一点是,为什么你传递元组中的最后两个参数? 你也可以使acc严格。
根据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)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.