Haskell 计数反转:弗雷格的StackOverflowerr,在哈斯克尔很好用

Haskell 计数反转:弗雷格的StackOverflowerr,在哈斯克尔很好用,haskell,frege,Haskell,Frege,我正试图计算一系列数字的倒数。下面的Frege程序适用于一小部分数字,但对100000个数字抛出StackOverflowerError import frege.IO inversionCount [] _ = (0, []) inversionCount [x] _ = (0, [x]) inversionCount xs n = (count, sorted) where count = lcount + rcount + mergecount (lcount, lsorted)

我正试图计算一系列数字的倒数。下面的Frege程序适用于一小部分数字,但对100000个数字抛出StackOverflowerError

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
import frege.IO
反转计数[]=(0,[])
反转计数[x]=(0,[x])
inversionCount xs n=(计数,排序)其中
计数=lcount+rcount+mergecount
(lcount,lsorted)=反转计数左lsize
(rcount,rsorted)=反转计数权rsize
(合并计数,排序)=反转合并计数lsorted lsize rsorted rsize(0,[]))
(左、右)=在中间X处拆分
mid=n`quot`2
lsize=mid
rsize=n-mid
inversionMergeCount xs[]uACC,排序)=(acc,反向排序++xs)
inversionMergeCount[]\uuys(acc,排序)=(acc,反向排序++ys)
反转合并计数(xs@(x:restx))m(ys@(y:resty))n(acc,已排序)
|xy=inversionMergeCount xs m resty(n-1)(acc+m,y:已排序)
|否则=inversionMergeCount restx(m-1)resty(n-1)(acc,x:y:sorted)
main(文件名:41;)=do
输入ST s(异常JScanner)
本机useDelimiter::JScanner->String->ST s JScanner
本机下一步::JScanner->ST s字符串
readFile f=do

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

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

如果这不起作用,请记住,使用诸如(-)、(+)等严格函数的应用程序更新的惰性参数会生成thunk,有时以后必须立即对其求值。这与
foldl
的问题相同,它看起来像inversionMergeCount和inversionCount的第二个参数

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


另一点是,为什么要在元组中传递最后两个参数?您还可以对acc进行严格的修改。

根据Ingo的建议,我进行了修改,代码现在正在运行。我还必须将计数设为
Integer
而不是
Int
,以避免Int溢出。以下是经过严格注释和
整数
转换的更新代码:

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)
inversionCount[].=(零,[]
反转计数[x]=(零,[x])
inversionCount xs n=(计数,排序)其中
计数=lcount+rcount+mergecount
(lcount,lsorted)=反转计数左lsize
(rcount,rsorted)=反转计数权rsize
(合并计数,排序)=反转合并计数lsorted lsize rsorted rsize zero[]
(左、右)=在中间X处拆分
mid=n`quot`2
lsize=mid
rsize=n-mid
inversionMergeCount xs[]33;!acc排序=(acc,反向排序++xs)
inversionMergeCount[]uys!acc排序=(acc,反向排序++ys)
inversionMergeCount(xs@(x:restx))!m(y:resty)n!!acc分类
|xy=inversionMergeCount xs m resty(pred n)(acc+m.big)(y:已排序)
|否则=inversionMergeCount restx(pred m)resty(pred n)acc(x:y:sorted)

看起来您正在通过表达式
反向排序++xs
反向排序++ys
inversionMergeCount
中构建大量Thunk。尝试用
将rs=反向排序放入rs
seq`xs
seq
(rs++xs)中`etcPerhaps这与
String
类型有关,在Frege中是严格的,但在Haskell?@MarimuthuMadasamy中是懒惰的,这提醒我编译器应该警告尾部递归函数中使用非平凡表达式更新的懒惰参数。试着写!在倒数第三个方程式中。另外,inversionCount的第二个参数应该是严格的。谢谢Ingo!在我对
acc
m
进行了严格的设置之后,它现在开始工作了。我还删除了不必要的元组。因为您在同一台机器上的Frege和Haskell中有相同的程序,数据相同-我想知道运行时间的差异,即优化Haskell的速度有多快?对于5次运行,平均来说,Frege需要2.56秒,优化Haskell需要1.34秒。嘿,这没那么糟糕。毕竟,在2.56秒内,JIT几乎没有做什么。是的,让JVM保持温暖一段时间会让统计变得有趣!
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)