为什么这个Haskell语句不能懒惰地评估?

per*_*iae 8 haskell lazy-evaluation ghci

我定义了以下函数:

ex 1 x = 1
ex 0 x = 0
ex b x = b ** x
Run Code Online (Sandbox Code Playgroud)

然后,当我执行以下操作时:

1 `ex` (sum [1..])
Run Code Online (Sandbox Code Playgroud)

它试图计算无限序列的总和,而不是懒惰和返回1.为什么?


编辑:经过进一步调查,我发现如果我ex在文件中定义函数会发生懒惰,但如果我在GHCI中定义它则不会:

$ ghci
GHCi, version 6.8.2: http://www.haskell.org/ghc/  :? for help
Loading package base ... linking ... done.
Prelude> let ex 1 x = 1
Prelude> let ex b x = b ** x
Prelude> ex 1 (sum [1..])
<interactive>: out of memory (requested 1048576 bytes)
Run Code Online (Sandbox Code Playgroud)

如果我将ex定义拉入文件(在本例中为test.hs):

$ ghci
GHCi, version 6.8.2: http://www.haskell.org/ghc/  :? for help
Loading package base ... linking ... done.
Prelude> :load test.hs 
[1 of 1] Compiling Main             ( test.hs, interpreted )
Ok, modules loaded: Main.
*Main> ex 1 (sum [1..])
1.0
Run Code Online (Sandbox Code Playgroud)

那么,新问题是为什么?

C. *_*ann 17

在GHCi中,每个let语句都引入了一个新的定义ex,而不是您期望的多个模式案例.所以它会挂起,因为当你ex 1 (sum [1..])之后输入时,只有最终ex b x = b ** x版本存在.

如果要在GHCi中定义具有多个模式案例的函数,则需要将其放在单个let语句中,如下所示:

let ex 1 x = 1; ex 0 x = 0; ex b x = b ** x
Run Code Online (Sandbox Code Playgroud)

这同样适用于通常跨多行写入的任何其他内容,例如do符号.例如,这样的函数:

f x = do
    y <- get
    put (x + y)
    return y
Run Code Online (Sandbox Code Playgroud)

必须在GHCi中这样写:

let f x = do { y <- get; put (x + y); return y }
Run Code Online (Sandbox Code Playgroud)

  • @perimosocordiae:作为GHCi的一个相当重的用户,我一般习惯于在外部文件中编写大部分或全部定义,使用GHCi加载文件,并且主要使用REPL来评估简单表达式. (6认同)
  • 您还可以使用`:set + m`在多行上写入定义. (2认同)