了解 PHP 数组排序行为

5

我有这个数组

$array = array(2, 1, "img1", "img2", "img10", 1.5, "3.14", "2.72");

应用sort函数后,它变成了:
sort($array);

Array
(
    [0] => 2.72
    [1] => 3.14
    [2] => img1
    [3] => img10
    [4] => img2
    [5] => 1
    [6] => 1.5
    [7] => 2
)

我不理解排序是如何执行的。
有人能解释一下如何得到输出数组吗?
编辑:
问题不在于我应该使用哪个“SORT FLAG”,而在于如何执行上述排序。在这里建议使用另一个“SORT FLAG”是无用的。

3
设置SORT_NUMERIC标志 http://php.net/sort - Pekka
@Pekka웃:我真的想知道即使使用SORT_REGULAR,排序是如何执行的。SORT_REGULAR是什么意思?排序规则如何为排序结果中的项目分配优先级? - Daric
我相信我已经找到了这个问题的答案,晚上回家后我会发布它(除非有人比我更快)。 - Scott
3个回答

6
第一个和第二个数字是实际的字符串(你用引号将它们传递),因此它们被视为:在字母排序中,数字排在任何字母之前。
其他数字是真正的数字(没有引号),因此该函数将它们分开,并按数字进行排序。
array(8)
    0 => '2.72'    [alphabetical]
    1 => '3.14'    [alphabetical]
    2 => 'img1'    [alphabetical]
    3 => 'img10'   [alphabetical]
    4 => 'img2'    [alphabetical]
    5 => 1         [numeric]
    6 => 1.5       [numeric]
    7 => 2         [numeric]

因此,该函数必须决定是否按字母顺序(a,b,c ...)或数字顺序(1,2,3..)对元素进行排序,因此它只需检查变量类型。
正如Pekka指出的那样,有一些标志可以设置来强制排序类型。它们在文档中有描述

编辑

这个理论在评论中被证明是错误的,我现在完全迷失了(:

这条评论 在文档中提出了一些有关此问题的有趣观点。


你能详细解释一下吗?我真的不明白,如果你能进一步解释,那会是一个很大的帮助。 - Daric
1
这并没有回答后端在使用什么比较算法,而我认为这就是问题所在。在期望的结果中,数字会排在字符串前面。这几乎适用于你能找到的所有排序,特别是由于ASCII值。为什么php要实现将数字排序第二呢?我已经检查了文档并运行了一些测试,sort()函数没有使用标准操作符(==,<,>),strcmp(),ord()。有人知道在没有标志的sort()实现中它使用了什么吗? - Scott
我想说的是,它似乎同时使用数字和字母排序,具体取决于您作为参数传递的类型。数字在字母排序中排在前面,但字母排序本身排在数字排序之前。 - Hugo Mota
2
让我向你展示你错了,看看@coanda提供的链接。如果你有机会对这个数组进行排序:("a","b","4",5,4,"true","TRUE",true, false, "c", "d")。然后再对这个数组进行排序:("a","b","4",5,4,"true","TRUE",true, false, "c")。仅仅通过删除"d"元素就可以得到两个完全不同的结果。根据PHP的说法,这不是一个bug,而是类型转换的结果。 - Scott
现在我也对混合类型数组的排序算法感到非常困惑,尝试使用各种排序标志对这种类型的数组进行排序。真的需要获得所有排序标志的实际排序算法。希望有人能提供相同的帮助。 - Daric
如果即使缓存机制也可能干扰顺序,那么当你混合类型时,可以说它是“不可预测的”。 - Hugo Mota

3

以Coanda链接中的信息为起点,可以清楚地看到PHP在比较不同类型的对象时使用了类型转换。问题在于理解在比较过程中将什么转换为什么,我还没有找到完整的列表,但通常情况下:

字符串与整数进行比较时会被转换为

(int) string <,>,=, (int) int 

在这种情况下,将字符串转换后变为0,因此所有的整数都大于一个字符串。(预期的结果)。这是我们需要关心的唯一情况,以进行此排序。
PHP使用快速排序,并且很可能选择其轴点作为array[n/2],其中n是数组中元素的数量。知道这两个信息后,我们对上述数组进行快速排序:
$pivot = $array[n/2] //n is the number of elements, this sets $pivot='img2'
//compare each element in the list (i am going to this by hand for demonstration)

(int) 'img2' < 2 //int to int comparison;'img2' resolves to 0 and 0 < 2
(int) 'img2' < 1 //int to int comparison;'img2' resolves to 0 and 0 < 1
      'img2' > 'img1' // string to string comparison; strcmp is +256 
      'img2' > 'img 10' //string to string comparison; strcmp is +256 
(float) 'img2' < 1.5 //float to float comparison;'img2' resolves to 0 and 0<1.5 
      'img2' > '3.14' //string to string comparison; strcmp is +54
      'img2' > '2.72' //string to string comparison; strcmp is +55

我们现在有两个新数组(一个用于大于,一个用于小于)。
 $greater = array('img1', 'img10', '3.14', '2.72);
 $less = array(2, 1, 1.5);

现在我们不需要进一步详细说明,因为我们无意中创建了两个包含所有容易比较对象的数组。 $greater 只有字符串,我们可以假设排序在此处正常工作,并将所有内容视为字符串。
sort($greater);
var_dump($greater);

产生
array(4) {
  [0]=>
  string(5) "2.72"
  [1]=>
  string(4) "3.14"
  [2]=>
  string(4) "img1"
  [3]=>
  string(5) "img10"
}

这正是我们预期的结果,也是上面的结果。我们对$lesser执行相同的操作。

$lesser = array(2, 1, 1.5);
sort($lesser);
var_dump($lesser);

我们将会获得以下内容:
array(3) {
  [0]=>
  int(1)
  [1]=>
  float(1.5)
  [2]=>
  int(2)
}

这也是预期的。现在,当我们将这三个数组连接在一起时(出于递归的目的,我将“img2”称为一个数组),我们得到了上面的结果。

Array
(
[0] => 2.72
[1] => 3.14
[2] => img1
[3] => img10
[4] => img2
[5] => 1
[6] => 1.5
[7] => 2
)

为了证明这一点,您可以按照相同的过程处理此相同的数组,但将$arr[3]替换为一个整数。
$arr = array("img2", 1, "img1", 2, "img10", 1.5, "3.14", "2.72");
sort($arr);
var_dump($arr)

由于数据透视表中的数据类型从字符串变成了整数,因此导致浮点字符串被解释为浮点数,从而得出完全不同的结果。

array(8) {
[0]=>
string(4) "img1"
[1]=>
string(5) "img10"
[2]=>
string(4) "img2"
[3]=>
int(1)
[4]=>
float(1.5)
[5]=>
int(2)
[6]=>
string(4) "2.72"
[7]=>
string(4) "3.14"
}

1
以下错误更深入地解释了排序混合类型:https://bugs.php.net/bug.php?id=21728
引用:
<?php
$arr1 = array("a","b","c","d","4",5,4,"true","TRUE",true);
sort($arr1);
var_dump($arr1);
?>

The output is :
array(10) {
  [0]=>
  bool(true)
  [1]=>
  int(4)
  [2]=>
  string(1) "4"
  [3]=>
  string(4) "TRUE"
  [4]=>
  string(1) "a"
  [5]=>
  string(1) "b"
  [6]=>
  string(1) "c"
  [7]=>
  string(1) "d"
  [8]=>
  string(4) "true"
  [9]=>
  int(5)
}

可能看起来很奇怪 - 为什么(int)5在所有字符串之后。这是因为"4"小于(int)5,"4"在"true"之前,而"true"在5之前。前两个很明显,第三个不太清楚,但没关系。最好不要混合使用数组中的类型。如果将5更改为"5",则"5"会在"4"之后。

1
当我有混合类型时,我会编写自己的排序函数。函数usort易于使用且效果显著。请参见链接。我认为PHP自带的排序函数很难预测其结果。 - Coanda

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接