作为一个相对较新的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)
答案 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 ()
你有countStairs
和main
都有意义。但是你也有这个叫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不知道它是否应该使用你的函数(因为<
只是常规函数,而不是特殊语法)或<
的内置(前奏)版本。
长话短说...使用警卫(或case
或if
)进行布尔测试,而不是模式匹配。
答案 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似乎是正确的。