Haskell 惰性评估和重用

发布于 2024-10-09 01:08:39 字数 459 浏览 2 评论 0原文

我知道如果我要在 Haskell 中计算一个正方形列表,我可以这样做:

squares = [ x ** 2 | x <- [1 ..] ]

然后当我像这样调用正方形时:

print $ take 4 squares

它会打印出 [1.0, 4.0, 9.0, 16.0]。其计算结果为 [ 1 ** 2, 2 ** 2, 3 ** 2, 4 ** 2 ]。现在,由于 Haskell 是函数式的,并且每次的结果都是相同的,如果我在其他地方再次调用 squares,它会重新评估已经计算出的答案吗?如果我在调用前一行后重新使用 squares,它会重新计算前 4 个值吗?

print $ take 5 squares

它会评估 [1.0, 4.0, 9.0, 16.0, 5 ** 2] 吗?

I know that if I were to compute a list of squares in Haskell, I could do this:

squares = [ x ** 2 | x <- [1 ..] ]

Then when I call squares like this:

print $ take 4 squares

And it would print out [1.0, 4.0, 9.0, 16.0]. This gets evaluated as [ 1 ** 2, 2 ** 2, 3 ** 2, 4 ** 2 ]. Now since Haskell is functional and the result would be the same each time, if I were to call squares again somewhere else, would it re-evaluate the answers it's already computed? If I were to re-use squares after I had already called the previous line, would it re-calculate the first 4 values?

print $ take 5 squares

Would it evaluate [1.0, 4.0, 9.0, 16.0, 5 ** 2]?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

绝不放开 2024-10-16 01:08:39

在这种情况下,不会重新计算,因为列表实际上已构建,并且正方形列表在调用后继续存在。然而,Haskell 函数一般不会被记忆。这只适用于这样的情况,即您没有显式调用函数,只是探索(内)有限列表。

In this case, it won't be recalculated because the list actually gets built, and the squares list continues to exist after the call. However, Haskell functions in general are not memoized. This only works for a case like this where you're not explicitly calling a function, just exploring an (in)finite list.

意犹 2024-10-16 01:08:39

这个值 squares 可能是多态的:

Prelude> :t [ x ** 2 | x <- [1 ..] ]
[ x ** 2 | x <- [1 ..] ] :: (Floating t, Enum t) => [t]

据我所知,它是否会被重新计算(在 GHC 中)取决于顶级值 squares 是否被赋予多态类型。我相信 GHC 不会对涉及类型类的多态值(从类型到值的函数)进行任何记忆,就像它不会对普通函数(从值到值的函数)进行任何记忆一样。

这意味着,如果您在此之前定义了 squares,

squares :: [Double]
squares = [ x ** 2 | x <- [1 ..] ]

squares 将仅计算一次,而如果您在

squares :: (Floating t, Enum t) => [t]
squares = [ x ** 2 | x <- [1 ..] ]

此之前定义了它,则可能会在每次使用时计算它,即使它是同一类型重复使用。 (不过,我还没有对此进行测试,如果 GHC 看到 squares :: [Double] 的多种用途,则可能会将 squares 值专门化为该值类型并共享结果值。)当然,如果 squares 用于多种不同类型,例如 squares :: [Double]squares :: [Float]< /code>,将会重新计算。

如果您没有为 squares 提供任何类型签名,则 单态限制将适用于它,除非您禁用它。结果将是 squares 被分配一个单态类型,这是从程序的其余部分推断出来的(或根据默认规则)。单态限制的目的正是为了确保那些看起来只会被评估一次的值,比如你的squares,实际上只会被评估一次。

This value squares is potentially polymorphic:

Prelude> :t [ x ** 2 | x <- [1 ..] ]
[ x ** 2 | x <- [1 ..] ] :: (Floating t, Enum t) => [t]

AFAIK, whether or not it will be recalculated (in GHC) depends on whether the top-level value squares is given a polymorphic type. I believe that GHC does not do any memoization of polymorphic values involving type classes (functions from types to values), just as it does not do any memoization of ordinary functions (functions from values to values).

That means if you define squares by

squares :: [Double]
squares = [ x ** 2 | x <- [1 ..] ]

then squares will only be computed once, while if you define it by

squares :: (Floating t, Enum t) => [t]
squares = [ x ** 2 | x <- [1 ..] ]

then it will likely be computed each time it is used, even if it's used repeatedly at the same type. (I haven't tested this, though, and it's possible that GHC, if it sees several uses of squares :: [Double], could specialize the squares value to that type and share the resulting value.) Certainly if squares is used at several different types, like squares :: [Double] and squares :: [Float], it will be recalculated.

If you don't give any type signature for squares, then the monomorphism restriction will apply to it, unless you have it disabled. The result will be that squares is assigned a monomorphic type, inferred from the rest of your program (or according to the defaulting rules). The purpose of the monomorphism restriction is exactly to ensure that values that look like they will only be evaluated once, such as your squares, really will only be evaluated once.

Saygoodbye 2024-10-16 01:08:39

为了澄清已经给出的答案,这是一个 Haskell 函数:

thisManySquares n = map (^2) [1..n]

因此,如果您用“thisManySquares 4”的调用替换您的 take 4 squares,那么是的,它会调用重复该功能。

To clarify an answer already given, this is a Haskell function:

thisManySquares n = map (^2) [1..n]

So, if you substituted calls of "thisManySquares 4" for your take 4 squares, then yes, it would call the function repeatedly.

夜空下最亮的亮点 2024-10-16 01:08:39

为什么不使用 ghci 来测试(如果 ghc 是你的编译器):

Prelude> let squares = [ x ** 2 | x <- [1 ..] ] :: [Float]
Prelude> :print squares
squares = (_t6::[Float])

所以现在所有 ghci 都知道你有一个列表。

Prelude> print $ take 4 squares
[1.0,4.0,9.0,16.0]
Prelude> :print squares
squares = 1.0 : 4.0 : 9.0 : 16.0 : (_t7::[Float])

知道它知道你的列表以 stuff 开头,因为它必须评估才能打印它。

Prelude> let z = take 5 squares
Prelude> :print z
z = (_t8::[Float])

take 5 本身不会评估任何内容,

Prelude> length z --Evaluate the skeleton
5
Prelude> :print z
z = [1.0,4.0,9.0,16.0,(_t9::Float)]

获取长度会导致 take 继续运行,我们看到您是对的。您也可以通过省略方块上的类型定义来测试多态时发生的情况。如果您不想使用 ghci,另一个好技巧是在代码中使用 undefined (您的程序在尝试计算 _|_ 时崩溃,其中 < code>undefined 是一种类型。)

Why not use ghci to test (if ghc is your compiler):

Prelude> let squares = [ x ** 2 | x <- [1 ..] ] :: [Float]
Prelude> :print squares
squares = (_t6::[Float])

So all ghci knows right now it that you have a list.

Prelude> print $ take 4 squares
[1.0,4.0,9.0,16.0]
Prelude> :print squares
squares = 1.0 : 4.0 : 9.0 : 16.0 : (_t7::[Float])

Know it knows that your list begins with stuff, because to it had to evaluate to print it.

Prelude> let z = take 5 squares
Prelude> :print z
z = (_t8::[Float])

take 5 by itself doesn't evaluate anything

Prelude> length z --Evaluate the skeleton
5
Prelude> :print z
z = [1.0,4.0,9.0,16.0,(_t9::Float)]

Taking the length causes take to run its course, and we see that you were right. You can test what's happening when its polymorphic as well, by just omitting the type definition on squares. Another good trick if you don't want to use ghci is to use undefined in your code (your program crashes exactly when it attempts to evaluate a _|_, which undefined is a type of.)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文