【问题标题】:Stack overflow in my recursive function我的递归函数中的堆栈溢出
【发布时间】:2015-01-06 05:00:09
【问题描述】:

代码在这里,当我调用 numberOf 3 或 numberOf integer>2 时,我得到了这个错误 ERROR - C stack overflow。我的代码应该将 2^(n-2) (2^n)-1 for n>2 之间的数字更改为 Binary 并检查是否有连续的 0 。如果有不计数,如果没有+1。

numberOf :: Integer -> Integer  
numberOf i = worker i

worker :: Integer -> Integer  
worker i  
    | (abs i) == 0 = 0   
    | (abs i) == 1 = 2  
    | (abs i) == 2 = 3   
    | otherwise = calculat (2^((abs i)-2)) ((2^(abs i))-2)

calculat :: Integer -> Integer -> Integer  
calculat ab bis  
    | ab == bis && (checker(toBin ab)) == True = 1  
    | ab < bis && (checker(toBin ab)) == True = 1 + (calculat (ab+1) bis)  
    | otherwise =  0 + (calculat (ab+1) bis)  

checker :: [Integer] -> Bool  
checker list  
    | list == [] = True  
    | 0 == head list && (0 == head(tail list)) = False  
    | otherwise = checker ( tail list)  

toBin :: Integer -> [Integer]  
toBin n  
   | n ==0 = [0]  
   | n ==1 = [1]  
   | n `mod` 2 == 0 = toBin (n `div` 2) ++ [0]  
   | otherwise = toBin (n `div` 2) ++ [1]    

测试:

numberOf 3 答案:(5)
数量 5 (13)
数量 10 (144)
numberOf (-5) (13)

【问题讨论】:

  • (+1) 用于标题中的堆栈溢出。

标签: haskell recursion stack-overflow


【解决方案1】:

问题在于您对calculat 的定义。你有ab == bisab &lt; bis 的情况,但是你调用calculat 的唯一地方是来自worker 的参数2^(abs i - 1)2^(abs i - 2)。由于第一个数字 (ab) 总是大于第二个数字 (bis),因此检查 ab &lt; bis 非常愚蠢。在您的其他情况下,您然后增加ab,确保此函数永远不会终止。你的意思是otherwise = calculat ab (bis + 1)吗?


你也可以彻底清理你的代码,有很多地方你做的很辛苦,或者添加了不必要的混乱:

-- Remove worker, having it separate from numberOf was pointless
numberOf :: Integer -> Integer
numberOf i
    | i' == 0 = 0
    | i' == 1 = 2
    | i' == 2 = 3
    -- Lots of unneeded parentheses
    | otherwise = calculat (2 ^ (i' - 1)) (2 ^ i' - 2)
    -- Avoid writing the same expression over and over again
    -- define a local name for `abs i`
    where i' = abs i

calculat :: Integer -> Integer -> Integer
calculat ab bis
    -- Remove unneeded parens
    -- Don't need to compare a boolean to True, just use it already
    | ab == bis && checker (toBin ab) = 1
    | ab <  bis && checker (toBin ab) = 1 + calculat (ab + 1) bis
    -- 0 + something == something, don't perform unnecessary operations
    | otherwise                       = calculat (ab + 1) bis

-- Pattern matching in this function cleans it up a lot and prevents
-- errors from calling head on an empty list
checker :: [Integer] -> Bool
checker []      = True
checker (0:0:_) = False
checker (_:xs)  = checker xs

-- Again, pattern matching can clean things up, and I find an in-line
-- if statement to be more expressive than a guard.
toBin :: Integer -> [Integer]
toBin 0 = [0]
toBin 1 = [1]
toBin n = toBin (n `div` 2) ++ (if even n then [0] else [1])

【讨论】:

  • 我想检查 2^((abs i)-2) 和 (2^(abs i))-2 之间的所有数字。这是我的错误。抱歉,现在我遇到了这个问题:程序错误:模式匹配失败:head []
  • 这是因为您正在调用head (tail list),而list 可能只有一个元素。一般来说,您应该更喜欢模式匹配而不是使用headtail,我将编辑我的帖子以显示您的代码已清理。
  • 非常感谢,我只是 Haskell 的初学者,还不够好。我还不知道哪些代码是不需要或需要的。再次感谢您在代码方面的建议。
  • 测试用例数为20,耗时较长,报C栈溢出错误。你有其他建议吗?
  • @Mert 对于numberOf 20,您将检查2^182^20-2 之间的每个数字,或总共786430 个数字。你的toBinchecker 函数不是特别有效,因为列表不是在这里使用的正确数据结构(Data.Sequence.Seq 可能是更好的选择,Data.Vector.Vector 会是最好的)。您还可以考虑使用Data.Bits 和一些花哨的布尔逻辑来提供帮助,这几乎肯定比自己做要快。
【解决方案2】:

在计算中,如果 ab == bis 但检查器返回 false,则您无法从函数中返回。

怎么样:

| ab >= bis && (checker(toBin ab)) == True = 1
| ab < bis && (checker(toBin ab)) == True = 1 + (calculat (ab+1) bis)  
| otherwise =  0 + (calculat (ab+1) bis)  
| ab >= bis = 0  
| ab < bis == True = 0 + (calculat (ab+1) bis)  
| otherwise =  0 + (calculat (ab+1) bis)  

【讨论】:

  • 添加 ab== bis 和检查器错误条件但仍然堆栈溢出
  • 现在我得到了:程序错误:模式匹配失败:head [] 使用此代码 | ab >= bis && (checker(toBin ab)) == True = 1 | ab
猜你喜欢
  • 2017-10-20
  • 1970-01-01
  • 1970-01-01
  • 2013-04-05
  • 2016-03-22
  • 2011-02-26
  • 1970-01-01
相关资源
最近更新 更多