为什么compareFunction必须考虑否定?

时间:2017-08-19 15:46:53

标签: javascript arrays sorting

Array.prototype.sort()

compareFunction(a, b)中,只有当我们需要交换a和b的位置时,我们才会返回正值。

如果省略if-statement中的否定compareFunctionArray.prototype.sort()仍然有效,那么开发人员为什么要写if-statement返回负值?



var list = [4, 5, 3, 5, 6, 9, 1, 4, 2];
list = list.sort(function(a, b) {
  if (a > b) {
    return 1;
  }
});
console.log(list); // correct result




4 个答案:

答案 0 :(得分:3)

这里的主要问题是你已经发明了自己的比较函数定义,而且基于这个问题:

  

在compareFunction(a,b)中,只有当我们需要交换a和b的位置时,我们才会返回正值。

这是不正确的。 “当我们需要交换a和b的位置时”是一个实现细节,并且您将实现与接口混淆。

compareFunction不负责指示何时应交换两个元素。它负责准确地传达两个要素的关系。排序算法对该信息的作用取决于实现者。如果您在某些时候只返回正确的值,那么您无法始终期望得到正确的结果。

例如,排序实现者可以实现这样的排序(基于https://www.nczonline.net/blog/2012/09/17/computer-science-in-javascript-insertion-sort/的示例)。如果我使用有效的比较函数运行它,它会产生正确的结果:

function insertionSort(items, compare) {

  var len = items.length, // number of items in the array
    value, // the value currently being compared
    i, // index into unsorted section
    j; // index into sorted section

  for (i = 0; i < len; i++) {

    // store the current value because it may shift later
    value = items[i];

    for (j = i - 1; j > -1 && compare(value, items[j]) < 0; j--) {
      items[j + 1] = items[j];
    }

    items[j + 1] = value;
  }

  return items;
}

console.log(insertionSort([4,2,6,1,7,2], (l, r) => l - r));

如果我用比较函数运行它,它什么都不做:

function insertionSort(items, compare) {

  var len = items.length, // number of items in the array
    value, // the value currently being compared
    i, // index into unsorted section
    j; // index into sorted section

  for (i = 0; i < len; i++) {

    // store the current value because it may shift later
    value = items[i];

    for (j = i - 1; j > -1 && compare(value, items[j]) < 0; j--) {
      items[j + 1] = items[j];
    }

    items[j + 1] = value;
  }

  return items;
}

console.log(insertionSort([4,2,6,1,7,2], function(a, b) {
    if (a > b) {
        return 1;
    }
}));

答案 1 :(得分:1)

这适用于您的情况,因为您没有测试所有可能性。但是,如果您查看the implementation内部,您会发现引擎在短阵列(即长度<= 10)上使用相同的算法而不是在较长的阵列上使用相同的算法。实际上,insertion sort用于短数组,而QuickSort用于长数组。

由于您的实施必须定义哪个数字高于,低于或等于另一个数字,因此当您需要忘记来实施&#39时,它会因为更长的数组而失败。 ;下方&#39; case(隐含相同的情况,因为当undefined which will be interpretedb >= a时,您的函数将返回0,因此QuickSort将无法正确排序数组因为它不能知道一个数字何时小于另一个数字,而插入排序将起作用,这要归功于它依赖于&#39;而不是&#39;比较如果我理解正确的话。

请参阅下面的示例:

&#13;
&#13;
var shortList = [9, 8, 7, 6, 5, 4, 3, 2, 1, 0],
    list = [10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0];
    
console.log('Works : ', shortList.sort(function(a, b) {
  if (a > b) {
    return 1;
  }
})); // You're being lucky on this one. Insertion sort.

console.log('Doesnt work : ', list.sort(function(a, b) {
  if (a > b) {
    return 1;
  }
})); // QuickSort

console.log('Works : ', list.sort(function(a, b) {
  if (a > b) {
    return 1;
  } else if (a < b) {
    return -1;
  }
  
  return a - b; // Can be reduced to 'return a - b';
})); // QuickSort
&#13;
&#13;
&#13;

答案 2 :(得分:0)

如果你不遵守规范,那么你很可能会看到引擎之间的不一致,因为浏览器(例如)不知道如何处理它。 Chrome,Firefox和Node.js似乎足以按预期对数组进行排序,但Safari并未对其进行排序,例如:

[4, 5, 3, 5, 6, 9, 1, 4, 2]

我希望所有这些浏览器在未达到规范时都会失败,例如“错误:RTM”。

答案 3 :(得分:0)

拥有所有三个案例(a&lt; b,a = b,a&gt; b)后,您可以拥有Total Ordering。但是,如果你只指定一个案例 - 只是一个&lt; b,你最终得到Weak Ordering。差异源于数学 - 但为了简化事情:使用以前的方案,你关心如何订购相互关联的元素,而对于后一种方案,你不要一定要小心。

var list = [
    { age: 65, name: 'Tony'},
    { age: 24, name: 'Joe'},
    { age: 24, name: 'Susan' } // Joe and Susan are tied,
    { age: 5, name: 'Alice'},
];

假设我们按照年龄对上述员工名单进行排序。总订单,我们保证订购:Alice,Joe,Susan,Tony。因此,尽管乔和苏珊的年龄相同,但他们的相对顺序在排序后仍然保留。然而,在弱排序的情况下,我们将首先使用Alice,将Tony放在最后,但Joe和Susan的顺序可供选择。这些员工年龄相同,因此是平等的。这对于弱排序是不利的,因为弱排序没有指定如何在结果中排​​序关系 - 这是不明确的!所以弱排序:我们可能会得到结果:Alice,Susan,Joe,Tony。当排序算法保留关联顺序时,我们说它是 stable sort

如果您的排序功能类似于Arrays.prototype.sort()并且需要总排序,那么总是提供所有三种情况!如果你不这样做:1。绑定的元素可能没有正确排序,2。浏览器可能会混淆,排序算法可能无法正确排序所有元素(即使没有任何联系!)。

// (*) Array.prototype.sort expects total ordering 
// ... so three cases needed
sort(list, function(a, b) { // (*)
    if (a.age < b.age) return -1;
    if (a.age > b.age) return  1;
    return 0;
});

如果您使用的排序功能期望弱排序,则只提供一个案例。 C ++标准库提供了一个期望弱排序的函数的完美示例。

// (*) C++ STL uses weak orderings ...
// ... so only one case needed
struct Employee { int age; string name };
vector<Employee> employees = {
    { 65, "Tony" },
    { 24, "Joe" },
    { 24, "Susan" },
    { 5, "Alice" }
};
struct Sorter {
    bool operator()(const Employee &e1, const Employee &e2) const {
        return e1.age < e2.age; // (*)
    }
};
sort(employees.begin(), employees.end(), Sorter());

在这里,您只需提供一个案例&lt; b在排序功能中。当您对元素进行排序并且存在平局时,它将如何排列绑定元素。来自C++ Reference for sort

  

comp ...返回的值表示作为第一个参数传递的元素是否被认为是在它定义的特定严格弱排序中的第二个参数之前...等效元素是无法保证保持其原始相对顺序

事实证明,C ++标准库的原始设计师Alexander Stepanov,或许did not want使用弱排序,并且更愿意使用总排序(如Javascript!) - 可能是为了防止这些含糊不清有关系。事实上,许多其他语言(包括JavaPython)使用排序函数的总排序。总排序很好,因为它们消除了歧义,所以你应该发挥作用来提供所有三种情况。