用JavaScript排序:每个比较函数都应该有一个“返回0”语句吗?

时间:2014-01-02 12:51:22

标签: javascript sorting

我最近阅读了许多关于JavaScript排序的答案,我经常偶然发现一个看起来像这样的比较函数:

array.sort(function(a,b){ a > b ? 1 : -1; });

因此,如果a大于b,则比较函数返回1;如果a小于等于b,则返回-1。如MDN(link)所述,比较函数也可以返回零,以确保两个项目的相对位置保持不变:

  

如果compareFunction(a,b)返回0,则保持a和b不变   相互尊重,但相对于所有不同的排序   元件。

所以官方的例子看起来更像是这样:

function compare(a, b) {
  if (a < b) return -1;
  if (a > b) return 1;
  return 0;
}

事实上,通过添加return 0语句,排序算法通常需要更少的迭代并且总计运行得更快(JSPerf)。

所以我想知道省略return 0声明是否有任何好处。

我意识到在MDN上,它也说:

  

注意:ECMAscript标准不保证这种行为,并且   因此并非所有浏览器(例如Mozilla版本至少可以追溯到   2003)尊重这一点。

引用行为,如果返回0,则ab应保持不变。那么也许,通过返回0,我们在不同的浏览器中得到一个稍微不同的排序数组?这可能是个原因吗?是否有任何其他充分理由不返回零?

2 个答案:

答案 0 :(得分:10)

  

所以我想知道在省略return 0语句方面是否有任何优势。

输入更少的字母。由于省略了一个比较,它可能会快一点。所有其他影响都是缺点

  

我意识到在MDN上,它也说:

     
    

注意:ECMAscript标准不保证这种行为,并且     因此并非所有浏览器(例如Mozilla版本至少可以追溯到     2003)尊重这一点。

  
     

指的是行为,如果为0,则a和b应保持不变   归还。

ab的位置可能保持不变只是stable sort的要求。这不是指定的行为,有些浏览器实现了非稳定的排序算法。

但是,返回零的实际目的是ab之前没有排序(好像小于0),ba之前排序(好像大于0) - 基本上当a等于b时。这是必须进行比较,所有排序算法都遵守它。

要生成有效,可满足的排序(数学上:将项目划分为totally ordered等价类),比较必须具有某些属性。它们在spec for sort中列为“一致比较函数”的要求。

最突出的是反身性,要求项a等于a(本身)。另一种说法是:

  

compare(a, a)必须始终返回0

比较函数不满足时会发生什么(就像你偶然发现的那样)?

规范说

  

如果comparefn [...]不是此数组元素的一致比较函数,sort的行为是实现定义的。

基本上意味着:如果提供无效的比较函数,则数组可能无法正确排序。它可能会被随机置换,或sort调用甚至可能无法终止。

  

所以也许,通过返回0,我们会略有不同   在不同浏览器中排序数组?这可能是个原因吗?

不,通过返回0,您可以跨浏览器获得正确排序的数组(由于排序不稳定,可能会有所不同)。原因是如果不返回0,你会得到略微不同的置换数组(如果有的话),甚至可能产生预期的结果,但通常是以更复杂的方式。

那么如果你没有为同等物品返回0会发生什么?有些实现没有问题,因为它们从不将项目与自身进行比较(即使在数组中的多个位置显而易见) - 可以优化这一点并省略对比较函数的代价高昂的调用,因为已知结果必须是0。

另一个极端是永不停止的循环。假设你有两个相同的项目,你会将后者与前者进行比较,并意识到你必须交换它们。再次测试,后者仍然比前者小,你必须再次交换它们。等等...

但是,一个高效的算法 尽管如此,它可能会做更多或更少的实际上不必要的掉期,因此需要比使用一致的比较功能更长的时间。

  

还有其他很好的理由不归零吗?

懒惰并希望数组不包含重复项。

答案 1 :(得分:3)

比较方法应遵守合同

Math.sign(compare(a, b)) = -Math.sign(compare(b, a))

如果在== b时返回非零答案,则违反该合同。