对于真实世界的自由项目,我从真实世界中进行了练习,其中的任务是为列表创建长度函数并将其与内部长度函数进行比较。
我建议的解决方案在https://github.com/Dierk/Real_World_Frege/blob/master/realworld/chapter3/I_Exercise_1.fr
下它的牛肉是:
mylength :: [a] -> Int
mylength (_:xs) = 1 + (mylength xs)
mylength [] = 0
-- optLength uses tail recursion and forces eager argument evaluation
optLength xs = privateLength xs 0 where
privateLength (_:rest) !count = privateLength rest (count + 1)
privateLength [] !count = count
main _ = do
assert (mylength []) (length []) "empty"
assert (mylength [0]) (length [0]) "one element"
-- assert (mylength [0..30_000]) (length [0..30_000]) "many elements lead to stack overflow"
assert (optLength [0..1_000_000]) (length [0..1_000_000]) "optLength can do a little more"
assert (optLength [0..10_000_000]) (length [0..10_000_000]) "this takes 40 seconds (20 s each)"
-- println (length [0..100_000_000]) -- never stops
我和内部长度函数都适用于100万条以下的列表,10米时速度非常慢,100毫米时根本不会停止。
弗雷格的内部长度函数(不是Haskell的)似乎有一个低于1亿的上限。是这样吗?
答案 0 :(得分:1)
这里有两个问题。
mylength
不会被尾递归,因此应该堆栈溢出。
以下证明长度函数本身没有限制(也适用于optlength
函数):
(TimeoutException是由于云服务的限制,而不是来自Frege。)
但是,您正在将length [0..100_000_000]
传递给assert
,看起来assert
也会懒散地获取他们的参数。不幸的是,这会导致对列表开头的引用保持活动状态。反过来,这会导致整个列表在内存中实现,因此事情变得非常缓慢,可能是因为你的堆在极限上并且GC试图为下一个参数找到一些空闲空间。
这与问题中的问题相同:https://github.com/Frege/frege/issues/65
试试-Xmx4g,我相信你的闪亮高级笔记本电脑没问题:)
一般的解决方案是在调用assert
之前强制执行结果,如:
let
n = length [0..100_000_000]
m = optlength [0..100_000_000]
in n `seq` m `seq` assert n m "should be equal"