Haskell中的多行是什么?一个运算符,一个函数,还有别的东西?

时间:2017-11-07 09:45:24

标签: haskell

我对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函数的结果,它们似乎至少导致了确切的结果相同的定义。

3 个答案:

答案 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)

所以你没有结束递归的声明。