为什么[x | x < - [1..10]]方法在Haskell中如此慢?

CYC*_*CYC 6 haskell

为什么这样的事情在Haskell中运行得非常慢?

test = [x|a<-[1..100],b<-[1..100],c<-[1..100],d<-[1..100],let x = a]

print $ length test
Run Code Online (Sandbox Code Playgroud)

只有大约10^8数字可以运行,它应该在眨眼间完成,但它似乎永远运行,几乎崩溃.

Rei*_*ton 5

你在ghci或编译程序中运行它吗?它有很大的不同.

如果在ghci中,那么ghci将保留test周围的计算值,以防您以后想要使用它.通常情况下这是一个好主意,但在这种情况下并不是test一个巨大的价值,无论如何重新计算都很便宜.多大?对于初学者来说,它是10 ^ 8个元素的列表,并且(在64位系统上)列表每个元素需要24个字节,因此已经是2.4G.然后是值本身的空间使用.人们可能会认为这些值都是从中获取的[1..100],因此它们应该被共享并且总共使用的空间可以忽略不计.但在列表中的值是真正的形式x,这可能取决于a,b,cd,和length从不检查列表中的值,因为它遍历它.因此,每个元件将被表示为指的是封闭件a,b,cd,这需要至少8*(4 + 1)= 40多个字节,使我们能够共6.4G的.

这是相当多的,当你分配6.4G的数据时,垃圾收集器必须进行大量的复制,所有数据都是永久存在的.这需要这么长时间,而不是实际计算列表或其长度.

如果你编译程序

test = [x|a<-[1..100],b<-[1..100],c<-[1..100],d<-[1..100],let x = a]

main = print $ length test
Run Code Online (Sandbox Code Playgroud)

然后test不必保持活动,因为它的长度正在计算中,因为很明显它永远不会被再次使用.所以现在GC几乎没有工作要做,程序运行几秒钟(合理的~10 ^ 8列表节点分配和计算Integer).