Jon*_*erg 11 haskell list-comprehension lazy-evaluation
我知道如果我要在Haskell中计算一个正方形列表,我可以这样做:
squares = [ x ** 2 | x <- [1 ..] ]
Run Code Online (Sandbox Code Playgroud)
然后我打电话给这样的广场:
print $ take 4 squares
Run Code Online (Sandbox Code Playgroud)
它会打印出[1.0,4.0,9.0,16.0].这被评估为[1**2,2**2,3**2,4**2].既然Haskell功能正常,每次结果都是相同的,如果我在其他地方再次调用square,它会重新评估它已经计算好的答案吗?如果我在调用前一行之后重新使用正方形,是否会重新计算前4个值?
print $ take 5 squares
Run Code Online (Sandbox Code Playgroud)
它会评估[1.0,4.0,9.0,16.0,5**2]吗?
Tyl*_*ves 19
在这种情况下,它不会被重新计算,因为列表实际上已构建,并且在调用后方块列表继续存在.但是,Haskell函数一般不会被记忆.这只适用于这样的情况,你没有显式调用函数,只是探索(in)有限列表.
Rei*_*ton 13
此值squares可能具有多态性:
Prelude> :t [ x ** 2 | x <- [1 ..] ]
[ x ** 2 | x <- [1 ..] ] :: (Floating t, Enum t) => [t]
Run Code Online (Sandbox Code Playgroud)
AFAIK,无论是否将重新计算(在GHC中)取决于顶级值squares是否为多态类型.我认为,GHC不会做涉及(从类型到值的函数)类型类多态值的任何记忆化,就像它不会做的普通函数(要将值值的函数)的任何记忆化.
如果你定义是指squares通过
squares :: [Double]
squares = [ x ** 2 | x <- [1 ..] ]
Run Code Online (Sandbox Code Playgroud)
那么squares只会计算一次,而如果你定义它
squares :: (Floating t, Enum t) => [t]
squares = [ x ** 2 | x <- [1 ..] ]
Run Code Online (Sandbox Code Playgroud)
然后它可能会在每次使用时计算,即使它在相同类型中重复使用.(尽管如此,我还没有对此进行测试,如果它看到GHC的几种用途squares :: [Double],可能会将该squares值专门化为该类型并共享结果值.)当然,如果squares在几种不同的类型中使用,例如squares :: [Double]和squares :: [Float],它将重新计算.
如果你没有给出任何类型的签名squares,那么单态限制将适用于它,除非你禁用它.结果将被squares分配一个单态类型,从程序的其余部分(或根据默认规则)推断出来.单态限制的目的正是为了确保看起来只会评估一次的值,例如你的squares,实际上只会被评估一次.