JavaScript中的排序:不应该返回一个布尔值足以用于比较函数?

我总是成功地排序我的数组这样(当我不想要标准词典排序):

var arr = […] // some numbers or so
arr.sort(function(a, b) {
    return a > b;
});

现在,有人告诉我这是错误的,我需要返回a-b。是真的,如果是真的,为什么?我测试了我的比较功能,它的工作原理!此外,为什么我的解决方案be so common当它是错误?

TL; DR

I have always successfully sorted my arrays like this

不,你没有。并没有注意到。快速反例:

> [1,1,0,2].sort(function(a, b){ return a>b })
Array [0, 1, 2, 1]
// in Opera 12. Results may vary between sorting algorithm implementations

why?

因为即使b大于a,你的比较函数也会返回false(或等同于0)。但是0意味着两个元素被认为是相等的 – 并且排序算法相信。

深入解释

JavaScript中的比较函数

How do comparison functions work?

Array::sort method可以采用可选的自定义比较函数作为其参数。该函数需要两个参数(通常称为a和b),它应该比较,并且应该返回一个数字

>> 0当a被认为大于b并且应该在它之后排序
> == 0,当a被认为等于b,无论哪个先出现都无关紧要
>& 0当a被认为小于b并且应该在它之前排序

如果它不返回一个数字,结果将被转换为一个数字(这对于布尔值很方便)。返回的数字不需要正好是-1或0或1(虽然通常是)。

一致排序

为了一致,比较函数将需要满足等式

comp(a, b) == -1 * comp(b, a)
// or, if values other than -1, 0 and 1 are considered:
comp(a, b) * comp(b, a) <= 0

如果该要求被打破,排序将表现为未定义。

引用ES5.1 spec on sort(ES6 spec中的同样的东西):

If comparefn is […] not a consistent comparison function for the elements of this array, the behaviour of sort is implementation-defined.

A function comparefn is a consistent comparison function for a set of values S if all of the requirements below are met for all values a, b, and c (possibly the same value) in the set S: The notation a <CF b means comparefn(a,b) < 0; a =CF b means comparefn(a,b) = 0 (of either sign); and a >CF b means comparefn(a,b) > 0.

Calling comparefn(a,b) always returns the same value v when given a specific pair of values a and b as its two arguments. Furthermore, Type(v) is Number, and v is not NaN. Note that this implies that exactly one of a <CF b, a =CF b, and a >CF b will be true for a given pair of a and b.

  • Calling comparefn(a,b) does not modify the this object.
  • a =CF a (07003)
  • If a =CF b, then b =CF a (07004)
  • If a =CF b and b =CF c, then a =CF c (07005 of =CF)
  • If a <CF b and b <CF c, then a <CF c (transitivity of <CF)
  • If a >CF b and b >CF c, then a >CF c (transitivity of >CF)

NOTE: The above conditions are necessary and sufficient to ensure that comparefn divides the set S into equivalence classes and that these equivalence classes are totally ordered.

Uh, what does this mean? Why should I care?

排序算法需要将数组的项彼此进行比较。要做一个好的,高效的工作,它不必需要比较每个项目,每个其他,但需要能够推理他们的订购。为了使其工作正常,有一些自定义比较函数需要遵守的规则。一个简单的一个是项目a等于它自己(compare(a,a)== 0) – 这是上面列表中的第一个项目(反身性)。是的,这是一个有点数学,但付出的好。

最重要的是传递性。它说,当算法已经比较了两个值a和b,以及b与c时,并且通过应用比较函数发现, a = b和b < c,则可以预期a < c也一样。这似乎只是合乎逻辑的,并且是定义明确,一致的排序所必需的。 但是你的比较函数确实失败了。让我们看看这个例子:

 function compare(a, b) { return Number(a > b); }
 compare(0, 2) == 0 // ah, 2 and 0 are equal
 compare(1, 0) == 1 // ah, 1 is larger than 0
 // let's conclude: 1 is also larger than 2

Ooops。这就是为什么当使用不一致的比较函数调用排序算法时,排序算法可能失败(在规范中,这是“实现相关的行为”,即不可预测的结果)。

Why is the wrong solution so common?

因为在许多其他语言中,有排序算法,不期望一个three-way comparison,但只是一个布尔小于操作符。 C++ std::sort就是一个很好的例子。如果需要确定相等性,它将简单地应用两次交换的参数。不可否认,这可以更有效,并且不易出错,但是如果操作符不能被内联,则需要更多的对比较函数的调用。

计数器示例

I have tested my comparison function, and it works!

只有通过运气,如果你尝试一些随机的例子。或者因为您的测试套件有缺陷 – 不正确和/或不完整。

这里是我用来找到上述最小反例的小脚本:

function perms(n, i, arr, cb) {
// calls callback with all possible arrays of length n
    if (i >= n) return cb(arr);
    for (var j=0; j<n; j++) {
        arr[i] = j;
        perms(n, i+1, arr, cb);
    }
}
for (var i=2; ; i++) // infinite loop
    perms(i, 0, [], function(a) {
        if (    a.slice().sort(function(a,b){ return a>b }).toString()
             != a.slice().sort(function(a,b){ return a-b }).toString() )
            // you can also console.log() all of them, but remove the loop!
            throw a.toString();
    });

什么比较函数是正确的?

当你想要一个词典排序时,根本不使用比较函数。如果需要,数组中的项目将被字符串化。

像关系运算符一样工作的通用比较函数可以实现为

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

通过几个技巧,这可以被缩小为等效函数(a,b){return(a> b)|| – (a For numbers,你可以简单地返回他们的差异,它遵守所有的法律:

function(a, b) {
    return a - b; // but make sure only numbers are passed (to avoid NaN)
}

如果你想反向排序,只需取适当的一个,并与b交换a。

如果要对复合类型(对象等)进行排序,请将每个a和每个b替换为相关属性的访问权限,或者方法调用或任何要排序的对象。

http://stackoverflow.com/questions/24080785/sorting-in-javascript-shouldnt-returning-a-boolean-be-enough-for-a-comparison

本站文章除注明转载外,均为本站原创或编译
转载请明显位置注明出处:JavaScript中的排序:不应该返回一个布尔值足以用于比较函数?