【问题标题】:Why does this Haskell statement not evaluate lazily?为什么这个 Haskell 语句不懒惰地评估?
【发布时间】:2010-07-08 17:43:27
【问题描述】:

我定义了以下函数:

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

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

【问题讨论】:

  • 不,1 ex (sum [1..]) 在我的电脑上返回 1.0。什么版本的 ghc 等?你得到与 ex 1 (sum [1..]) 相同的结果吗?
  • @ShreevatsaR:好点,见我上面的更新。

标签: haskell lazy-evaluation ghci


【解决方案1】:

在 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 }

【讨论】:

  • 啊哈,真是不方便。不过很高兴知道,谢谢!
  • @perimosocordiae:作为 GHCi 的重度用户,我通常养成将大部分或全部定义写入外部文件、使用 GHCi 加载文件并使用 REPL 的习惯主要用于评估简单的表达式。
  • 您也可以使用:set +m 将定义写入多行。
  • @aleator:我相信比这篇文章更新的功能。考虑到新的 GHC 功能和库,我一想到现在有多少答案是愚蠢的,我不寒而栗。
【解决方案2】:
Prelude> let ex 1 x = 1
Prelude> let ex b x = b ** x

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

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

【讨论】:

    【解决方案3】:

    我错过了一点懒惰,导致下面的答案是错误的。


    因为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.
    

    【讨论】:

    • -1:它只会在实际需要时评估总和,而在这种情况下不需要。
    猜你喜欢
    • 2015-09-06
    • 2021-07-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-14
    • 2019-11-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多