为什么这样的事情在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
数字可以运行,它应该在眨眼间完成,但它似乎永远运行,几乎崩溃.
你在ghci或编译程序中运行它吗?它有很大的不同.
如果在ghci中,那么ghci将保留test
周围的计算值,以防您以后想要使用它.通常情况下这是一个好主意,但在这种情况下并不是test
一个巨大的价值,无论如何重新计算都很便宜.多大?对于初学者来说,它是10 ^ 8个元素的列表,并且(在64位系统上)列表每个元素需要24个字节,因此已经是2.4G.然后是值本身的空间使用.人们可能会认为这些值都是从中获取的[1..100]
,因此它们应该被共享并且总共使用的空间可以忽略不计.但在列表中的值是真正的形式x
,这可能取决于a
,b
,c
和d
,和length
从不检查列表中的值,因为它遍历它.因此,每个元件将被表示为指的是封闭件a
,b
,c
和d
,这需要至少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
).