我对Haskell很新,我必须说我很困惑
我正在使用GHCi前奏
首先尝试创建一个阶乘
Prelude> factorial 0 = 1
Prelude> factorial n = n*factorial(n-1)
Prelude> factorial 2
*** Exception: stack overflow
以堆栈溢出结束。显然递归并没有停止。
Prelude> :t factorial
factorial :: Num t => t -> t
然后阅读这篇文章How to define a function in ghci across multiple lines?
我发现我必须使用多行版本或大括号(顺便说一句,这是一个运算符吗?)
Prelude> let { fact 0 = 1 ; fact n = n * fact (n-1) }
Prelude> fact 5
120
Prelude> ::t fact
fact :: (Eq p, Num p) => p -> p
或
Prelude> :{
Prelude| facto 0 = 1
Prelude| facto n = n*facto(n-1)
Prelude| :}
Prelude> facto 4
24
Prelude> :t facto
facto :: (Eq p, Num p) => p -> p
所以,我的问题是,为什么第一个错误,在这种情况下会发生什么,为什么第二个和第三个工作,并且从:t函数的结果,它们似乎至少导致了确切的结果相同的定义。
答案 0 :(得分:5)
为什么第一个错误,在这种情况下会发生什么
因为您定义了具有相同名称的两个功能。
首先定义:
factorial 0 = 1
稍后你定义:
factorial n = n*factorial(n-1)
但是Haskell会将第二个因子视为一个更加局部化的变量,因此第二个函数定义隐藏前一个。因此,第一行(factorial 0 = 1
)不再是定义的一部分。因此,Haskell将评估factorial 2 -> 2 * factorial 1 -> 2 * 1 * factorial 0 -> 2 * 1 * 0 * factorial (-1) -> ...
。
为什么第二和第三个正在运作
因为在这里你定义了一个单个函数,Haskell将这两个子句解释为相同函数的两个子句。与:t function
获得相同的事实只是巧合。
请注意,上述内容仅适用于GHCi。如果您使用ghc
编译器,它当然会将您的所有语句视为同一函数定义的一部分。如果您混合使用两个函数的子句(例如,第一个a 0 = 0
,然后是b 0 = 0
,然后是a n = n
),则会对同一函数的多个定义产生错误。
答案 1 :(得分:4)
在早期版本的ghci
中,定义函数的行必须以let
为前缀。从最近的版本开始,let
隐含在任何定义行中
这意味着,定义你的函数的每一行都被视为它自己的let
表达式,因此每个后续行替换(或者#39;阴影')前一个定义,而不是添加它在常规的Haskell程序中。
:{
中的:}
和ghci
允许您将多行作为单个输入写入,而通常每行都在ghci
中独立处理。这意味着您可以编写多行let
表达式:
:{
let fact 0 = 1
fact n = n * fact (n - 1)
:}
或者,在以后的版本中,这是等效的:
:{
fact 0 = 1
fact n = n * fact (n - 1)
:}
函数fact
将被定义为在常规Haskell程序中所期望的。
答案 2 :(得分:1)
定义时
Prelude> factorial 0 = 1
Prelude> factorial n = n*factorial(n-1)
Prelude> factorial 2
*** Exception: stack overflow
factorial
的第一个定义被废弃,因此该函数被定义为
Prelude> factorial n = n*factorial(n-1)
所以你没有结束递归的声明。