以下要点中的代码几乎逐字逐句地从Martin Odersky的 Scala中的函数式编程原则课程Coursera的讲座中解读:
https://gist.github.com/aisrael/7019350
问题发生在第38行,在union
课程NonEmpty
的定义中:
def union(other: IntSet): IntSet =
// The following expression doesn't behave associatively
((left union right) union other) incl elem
使用给定的表达式,((left union right) union other)
,largeSet.union(Empty)
需要花费过多的时间来完成包含100个元素或更多元素的集合。
当该表达式更改为(left union (right union other))
时,联合操作将相对立即完成。
ADDED :这是一个更新的工作表,显示即使对于具有随机元素的较大集合/树,表达式((左∪右)∪其他)可以永远占用但是(左∪(右∪)其他))将立即完成。
答案 0 :(得分:5)
您的问题的答案与关系数据库密切相关 - 以及他们做出的明智选择。当一个数据库“联合”表 - 一个智能控制器系统会做出一些决定,比如“表A有多大?首先加入A&amp; B或者用户写入时A&amp; C更有意义:< / p>
A Join B Join C
无论如何,当您手动编写代码时,您不能指望相同的行为 - 因为您已使用括号指定了您想要的顺序。这些聪明的决定都不会自动发生。 (虽然理论上他们可以,这就是Oracle,Teradata,mySql存在的原因)
考虑一个非常大的例子:
Set A - 1 Billion Records
Set B - 500 Million Records
Set C - 10 Records
为了论证,假设union运算符通过连接的2个集合的SMALLEST获取O(N)个记录。这是合理的,每个键可以作为散列检索在另一个中查找:
A&amp; B运行时= O(N)运行时= 500万 (我们假设这个类很聪明,可以使用两者中较小的一个进行查找)
所以
(A & B) & C
Results in:
O(N) 500 million + O(N) 10 = 500,000,010 comparisons
再次指出,它被迫将每个内部括号中的10亿条记录与5亿条记录进行比较,然后再拉10条记录。
但请考虑一下:
A & (B & C)
现在好奇的事情发生了:
(B & C) runtime O(N) = 10 record comparisons (each of the 10 C records is checked against B for existence)
then
A & (result) = O(N) = 10
Total = 20 comparisons
请注意,一旦(B&amp; C)完成,我们只需要打10条记录而不是10亿条!
两个例子都会产生完全相同的结果;一个在O(N)= 20运行时,另一个在500,000,010!
总而言之,这个问题只是一小部分说明了数据库设计中的一些复杂思维以及该软件中发生的智能优化。这些东西并不总是在编程语言中自动发生,除非你用这种方式编码,或者使用某种类型的库。例如,您可以编写一个需要多个集合的函数,并智能地决定联合顺序。但是,如果必须混入其他一系列操作,这个问题就会变得难以置信。希望这会有所帮助。
答案 1 :(得分:2)
关联性与性能无关。两个表达式可能是相关性的等价物,但实际上计算可能比另一个更难:
(23 * (14/2)) * (1/7)
与
相同23 * ((14/2) * (1/7))
但是,如果是我评估这两个,我会在第二个问题上得到答案(23),但如果我强迫自己只使用第一个,那就需要更长的时间。