Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/perl/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Haskell Euler 23项目:需要了解此堆叠溢出程序_Haskell_Stack Overflow - Fatal编程技术网

Haskell Euler 23项目:需要了解此堆叠溢出程序

Haskell Euler 23项目:需要了解此堆叠溢出程序,haskell,stack-overflow,Haskell,Stack Overflow,嗨,伙计们。我目前正在做的工作。我在atm的地方是,我的代码对我来说似乎是正确的——不是“好算法”的意思,而是“应该工作”的意思——但会产生堆栈内存溢出 我知道我的算法并不完美(特别是我可以避免在我的worker函数的每个递归步骤中计算如此大的中间结果) 不过,在学习Haskell的过程中,我想了解为什么这段代码会失败得如此惨烈,以便下次避免此类错误 任何关于此程序错误原因的见解都将受到赞赏 import qualified Data.List as Set ((\\)) main = pri

嗨,伙计们。我目前正在做的工作。我在atm的地方是,我的代码对我来说似乎是正确的——不是“好算法”的意思,而是“应该工作”的意思——但会产生堆栈内存溢出

我知道我的算法并不完美(特别是我可以避免在我的
worker
函数的每个递归步骤中计算如此大的中间结果)

不过,在学习Haskell的过程中,我想了解为什么这段代码会失败得如此惨烈,以便下次避免此类错误

任何关于此程序错误原因的见解都将受到赞赏

import qualified Data.List as Set ((\\))

main = print $ sum $ worker abundants [1..28123]

-- Limited list of abundant numbers
abundants :: [Int]
abundants = filter (\x -> (sum (divisors x)) - x > x) [1..28123]

-- Given a positive number, returns its divisors unordered.
divisors :: Int -> [Int]
divisors x  | x > 0     =   [1..squareRoot x] >>=
                            (\y ->  if      mod x y == 0
                                    then    let d = div x y in
                                            if y == d
                                            then [y]
                                            else [y, d]
                                    else    [])
            | otherwise = []


worker :: [Int] -> [Int] -> [Int]
worker (a:[]) prev = prev Set.\\ [a + a]
worker (a:as) prev = worker as (prev Set.\\ (map ((+) a) (a:as)))


-- http://www.haskell.org/haskellwiki/Generic_number_type#squareRoot
(^!) :: Num a => a -> Int -> a
(^!) x n = x^n

squareRoot :: Int -> Int
squareRoot 0 = 0
squareRoot 1 = 1
squareRoot n =
   let twopows = iterate (^!2) 2
       (lowerRoot, lowerN) =
          last $ takeWhile ((n>=) . snd) $ zip (1:twopows) twopows
       newtonStep x = div (x + div n x) 2
       iters = iterate newtonStep (squareRoot (div n lowerN) * lowerRoot)
       isRoot r  =  r^!2 <= n && n < (r+1)^!2
   in  head $ dropWhile (not . isRoot) iters
导入符合条件的数据。列表为集合(\\)
main=打印$sum$工人数量[1..28123]
--数量有限
丰度::[Int]
丰度=过滤器(\x->(总和(除数x))-x>x)[1..28123]
--给定一个正数,无序返回其除数。
除数::Int->[Int]
除数x | x>0=[1..平方根x]>>=
(\y->如果mod x y==0
然后让d=divxy进入
如果y==d
然后[y]
else[y,d]
其他[])
|否则=[]
工作者::[Int]->[Int]->[Int]
辅助(a:[])上一个=上一个集合。\\[a+a]
辅助(a:as)prev=辅助(prev Set.\\(映射(+)a)(a:as)))
-- http://www.haskell.org/haskellwiki/Generic_number_type#squareRoot
(^!)::numa=>a->Int->a
(^!)xn=x^n
平方根::Int->Int
平方根0=0
平方根1=1
平方根n=
让twopows=迭代(^!2)2
(洛维罗,洛维恩)=
最后$takeWhile((n>=).snd)$zip(1:twopows)twopows
新步骤x=div(x+divnx)2
iters=迭代新步骤(平方根(div n lowerN)*lowerRoot)

isRoot r=r^!2将来,自己尝试一点简约是礼貌的。例如,通过一点播放,我发现以下程序也会出现堆栈溢出(使用8M堆栈):

…这真的确定了什么样的功能在欺骗你。让我们来看看<代码>工作人员>代码>:< /P>
worker (a:[]) prev = prev Set.\\ [a + a]
worker (a:as) prev = worker as (prev Set.\\ (map ((+) a) (a:as)))
即使在我第一次阅读时,这个函数在我的脑海中也是红色的,因为它是尾部递归的。Haskell中的尾部递归通常不像其他语言那样是一个好主意;保护递归(在递归之前至少生成一个构造函数,或者在生成构造函数之前递归一小段时间)通常更适合于延迟计算。事实上,这里发生的是,对
worker
的每个递归调用都在
prev
参数中构建一个更深层次的嵌套thunk。当最终返回
prev
时,我们必须深入到一长串
集合中。\\
调用以确定我们最终得到了什么

这个问题被明显的严格性注释没有帮助这一事实稍微弄糊涂了。让我们按摩
工人
,直到它起作用。第一个观察结果是第一个从句完全被第二个从句所包含。这是风格上的;它不应该影响行为(空列表除外)

现在,显而易见的严格性注释:

worker []     prev = prev
worker (a:as) prev = prev `seq` worker as (prev Set.\\ map (a+) (a:as))
我惊讶地发现,这仍然是堆栈溢出!狡猾的是,列表中的
seq
的计算结果仅足以了解列表是否匹配
[]
\:
。以下情况不会导致堆栈溢出:

import Control.DeepSeq

worker []     prev = prev
worker (a:as) prev = prev `deepseq` worker as (prev Set.\\ map (a+) (a:as))
我没有将这个最终版本插入到原始代码中,但它至少可以与上面最小化的
main
一起工作。顺便说一句,您可能喜欢以下实现思想,这也会导致堆栈溢出:

import Control.Monad

worker as bs = bs Set.\\ liftM2 (+) as as
但是可以通过使用
数据.Set
而不是
数据.List
来修复,并且没有严格的注释:

import Control.Monad
import Data.Set as Set

worker as bs = toList (fromList bs Set.\\ fromList (liftM2 (+) as as))

好的,我把它装上子弹,试了一下。丹尼尔·瓦格纳的建议很好,可能比我的好。问题确实出在worker函数上,但我建议使用Data.MemoCombinators来记忆函数

而且,你的除数算法有点傻。有一个更好的方法可以做到这一点。这是一种mathy,需要很多TeX,因此这里有一个指向math.stackexchange页面的链接,该页面介绍了如何实现这一点。我所说的是一个公认的答案,尽管其他人给出了一个递归解决方案,我认为它运行得更快。(它不需要素因子分解。)

问题是

worker (a:as) prev = worker as (prev Set.\\ (map ((+) a) (a:as)))
生成嵌套不好的thunk。通过利用
worker
的两个参数都在此应用程序中排序的事实,您可以避免这种情况,并获得比使用
deepseq
更好的性能。因此,您可以通过注意到
prev
中小于
2*a
的任何内容都不能是两个丰富数字的总和来获得增量输出,因此

worker (a:as) prev = small ++ worker as (large Set.\\ map (+ a) (a:as))
  where
    (small,large) = span (< a+a) prev

(或使用软件包的版本),计算设置的差异是O(长度),而不是O(长度^2)。

您能公布准确的错误吗?我在Haskell中从未见过真正的堆栈溢出。空间泄漏?而且,你对牛顿方法的实现真的让我困惑。你为什么不使用包含的sqrt函数?@Josh:我编辑了我的OP来回答你好吧,这是空间泄漏。发生这种情况时,您运行什么函数?通常这意味着您要么需要记忆该函数,要么将其重写为尾部递归。@Josh:当我运行调用
worker
循环的
:main
函数时,就会发生这种情况。对我来说,
worker
递归似乎能够实现TCO,这就是为什么我感到惊讶:(只是为了尝试一个低垂的果实,你有没有尝试过
-O2
编译键?谢谢你的回答,但这有点离题,因为我特别寻找
worker (a:as) prev = worker as (prev Set.\\ (map ((+) a) (a:as)))
worker (a:as) prev = small ++ worker as (large Set.\\ map (+ a) (a:as))
  where
    (small,large) = span (< a+a) prev
minus xxs@(x:xs) yys@(y:ys)
    = case compare x y of
        LT -> x : minus xs yys
        EQ -> minus xs ys
        GT -> minus xxs ys
minus xs _ = xs             -- originally forgot the case for one empty list