递归haskell和堆栈溢出

时间:2011-12-31 04:54:38

标签: haskell

作为一个相对较新的Haskell和函数式编程的人,主要来自Python背景,我想知道为什么以下函数会导致Haskell中的堆栈溢出,即使我使用像4这样的非常低的数字,或者5作为输入变量,而Python中完全相同的函数可以处理20或更高的整数而不会溢出。为什么会这样?

countStairs <0 = 0
countStairs 0 = 1
countStairs n = countStairs(n-1) + countStairs(n-2) + countStairs(n-3)

我已经阅读了有关Haskell和堆栈溢出的其他响应,这些响应解决了代码优化问题并解决了特定的溢出代码,而我有兴趣了解这两种语言在这里如何处理递归的差异的具体原因,或更普遍的原因Haskell代码导致堆栈溢出。

编辑:我没有包含完整的python代码,因为这是我在Stack Overflow中的第一个问题,我正在努力弄清楚如何让我的Python正确格式化(很受欢迎,有些人,顺便说一下)。在这里,格式不佳,但所有,但所写的Python确实可以正常使用整数20,而我无疑可怜的Haskell没有。我编辑了Haskell代码,以显示我最初省略的相应代码。我以为我包含了相关的递归部分,但显然我省略了基本情况。不过,正如所写,我的Haskell堆栈溢出而我的Python没有,我仍然对学习原因感兴趣。虽然我不是来自编程背景,但我真的很喜欢学习Haskell而只是想学习更多。感谢那些尽管我的问题不完整而试图解决这个问题的人。

def countStairs(n):
    if n < 0:
        return 0
    elif n == 0:
        return 1
    else:
        return countStairs(n-1) + countStairs(n-2) + countStairs(n-3)

myint = int(raw_input("Please enter an integer: "))
print countStairs(myint)

3 个答案:

答案 0 :(得分:10)

添加终止条件的另一种方法是使用一个保护(这也解决了Corbin提到的<= 2条件:

countStairs n | n > 0 = countStairs(n-1) + countStairs(n-2) + countStairs(n-3)
              | otherwise = 1

<强>更新

您更新的Haskell示例不起作用,因为您误解了模式匹配的工作原理。你期望它像一个守卫一样工作(当你试图在模式匹配中提供一个布尔表达式< 0时),但是,你的函数版本永远不会匹配(当你调用countStairs函数时)。考虑这个例子:

countStairs < 0 = "Matched '< 0'"
countStairs   0 = "Matched '0'"
countStairs   n = "Matched n"

main = do
    putStrLn $ countStairs (-1)  -- outputs: "Matched n"
    putStrLn $ countStairs 0     -- outputs: "Matched 0"
    putStrLn $ countStairs 20    -- outputs: "Matched n"

这里有趣的是你的函数实际编译。要找出原因,请将上述代码加载到ghci中并键入:browse。这将为您提供您在此模块中定义的功能列表。你应该看到这样的东西:

(Main.<) :: Num a => t -> a -> [Char]
countStairs :: Num a => a -> [Char]
main :: IO ()

你有countStairsmain都有意义。但是你也有这个叫Main.<的函数。这是什么?您已在此模块中重新定义了<函数!如果您不熟悉,可以定义中缀函数(如+<>等),如下所示:

infix <
a < b = True

-- also defined as
(<) a b = True

通常,您需要infix FUNCTION_NAME来表示您的函数是中缀。但是..前奏已经将<定义为中缀函数,因此,您不需要,而只是给出了您自己的<定义。

现在,让我们像countStairs < 0 = "Matched '< 0'"一样重新排列a < b,你就明白了:

(<) countStairs 0 = "Matched '< 0'"

在此函数中,countStairs实际上是<函数的第一个参数。

这里还有一个例子可以说明这一点。尝试在ghci中运行1 < 0(模块仍然加载)。这是你会得到的:

*Main> 1 < 0

<interactive>:1:3:
    Ambiguous occurrence `<'
    It could refer to either `Main.<', defined at foo.hs:3:13
                          or `Prelude.<', imported from Prelude

通常情况下,你会得到False,但在这种情况下,ghci不知道它是否应该使用你的函数(因为<只是常规函数,而不是特殊语法)或<的内置(前奏)版本。

长话短说...使用警卫(或caseif)进行布尔测试,而不是模式匹配。

答案 1 :(得分:8)

不太熟悉haskell,但看起来没有终止条件。我相信在接近负无穷大的情况下会继续前进。

尝试类似:

countStairs 0 = 1
countStairs n = countStairs(n-1) + countStairs(n-2) + countStairs(n-3)

这意味着countStairs(0)= 1。

如果您打算调用countStairs(n)|,您可能还需要担心否定n&lt; = 2。

答案 2 :(得分:8)

使用此:

countStairs n | n <= 0 = 1
countStairs n = countStairs(n-1) + countStairs(n-2) + countStairs(n-3)

在GHCi中给我这个:

∀x. x ⊢ countStairs 20
289329

......大约两秒钟。所以是的,Corbin似乎是正确的。