8

我定义了以下函数:

ex 1 x = 1
ex 0 x = 0
ex b x = b ** x

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

1 `ex` (sum [1..])

它试图计算无限序列的总和,而不是懒惰并返回 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)

如果我将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

那么,新的问题是为什么?

4

3 回答 3

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

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

f x = do
    y <- get
    put (x + y)
    return y

在 GHCi 中必须这样写:

let f x = do { y <- get; put (x + y); return y }
于 2010-07-08T17:55:56.787 回答
1
Prelude> let ex 1 x = 1
Prelude> let ex b x = b ** x

您在这里没有定义具有两种情况的函数。你用一种情况定义一个函数,然后你再次定义它,覆盖之前的定义。

要使用两种模式定义一个函数,请使用let ex 1 x = 1; ex b x = b ** x,即用分号分隔大小写。

于 2010-07-08T17:57:30.843 回答
-2

我错过了一点懒惰,这使得下面的答案是错误的。


因为sum计算序列中所有元素的总和。在你的情况下,这是无穷无尽的。

你可能想要

map ((curry ex) 1) [1..]

那是

map -- map each item x to y
    (
        (
            curry ex -- curry ex, to transform (x, y) -> z into x -> y -> z
        )
        1 -- then invoke it with 1, which results in y -> z, x being 1
    )
    [1..] -- the infinite sequence to be mapped.
于 2010-07-08T17:52:12.837 回答