【问题标题】:What is the multiple line in Haskell ? an operator , a function , something else?Haskell 中的多行是什么?一个操作符,一个函数,还是别的什么?
【发布时间】:2017-11-07 09:45:23
【问题描述】:

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

【问题讨论】:

  • 不,它只是语法。
  • :{:} 甚至都不是语法。它们是 GHCi 的命令。
  • 我发现编辑文本文件然后(重新)加载到 ghci 中更方便。不需要所有这些括号,也不需要像这样的惊喜!

标签: haskell


【解决方案1】:

为什么第一个是错的,在这种情况下会发生什么

因为您定义了两个同名的函数

首先你定义:

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) -> ...

为什么第 2 次和第 3 次有效

因为这里定义了一个single 函数,而Haskell 将这两个子句解释为same 函数的两个子句。与:t function 获得相同的事实只是巧合。

请注意,以上仅对 GHCi 有效。如果您使用ghc 编译器,它当然会将您的所有语句视为同一函数定义的一部分。如果您混合使用两个函数的子句(例如,首先是a 0 = 0,然后是b 0 = 0,然后是a n = n),则会出现*同一个函数的多个定义的错误)。

【讨论】:

  • 新的 haskell 用户可能不清楚这仅适用于 GHCi,而不适用于编译程序。
【解决方案2】:

ghci 的早期版本中,定义函数的行必须以let 开头。在最近的版本中,let 隐含在任何定义行中。
这意味着,定义函数的每一行都被视为它自己的 let 表达式,因此每一行后续的行都会替换(或“阴影”)之前的定义,而不是像常规 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 程序中所期望的那样。

【讨论】:

  • 是的,GHCi 过去常常要求定义以let 开头。从 GHC-8 开始,这个可以省略。像这样的问题让我怀疑这是否真的是一个好决定......
  • 关于:{ :}:它不是 Haskell 语法,它是 GHCi-only 语法,允许多行输入。
  • :{:} 与您的 GHCi 版本是否需要明确的 let 关键字无关;它们允许您将命令跨越多个物理行。
【解决方案3】:

当你定义时

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)

所以你不再有结束递归的语句了。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-04-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多