PHP的USORT回调函数参数

12

这是一个非常深奥的问题,但我真的很好奇。今天是多年来我第一次使用usort函数,我特别想知道到底发生了什么。假设我有以下数组:

$myArray = array(1, 9, 18, 12, 56);
我可以使用usort来排序这个数组:
usort($myArray, function($a, $b){
  if ($a == $b) return 0;
  return ($a < $b) ? -1 : 1;
});

我不完全清楚两个参数$a和$b究竟是什么。它们代表什么?我是指,我可以假设$a代表数组中的当前项,但这与什么进行比较呢?$b又是什么?

我可以将我的数组扩展到包括字符串:

$myArray = array(
  array("Apples", 10),
  array("Oranges", 12),
  array("Strawberries", 3)
);

然后运行以下代码:

usort($myArray, function($a, $b){
  return strcmp($a[0], $b[0]);
});

这将按照[0]索引值对我的子数组进行字母顺序排序。但是这并没有提供任何关于$a和$b是什么的明确说明。我只知道它们匹配我正在寻找的模式。

有人能提供一些关于实际发生的事情的澄清吗?


+1 我一直都这么想。 - alex
3个回答

31
$a和$b的确切定义取决于用于对数组进行排序的算法。要对任何内容进行排序,都必须有一种比较两个元素的方法,这就是回调函数的作用。一些排序算法可以从数组的任何位置开始,而其他排序算法只能从特定部分开始,因此$a和$b没有固定的含义,除了它们是数组中需要根据当前算法进行比较的两个元素。

此方法可用于解析PHP使用的算法。

<?php

$myArray = array(1, 19, 18, 12, 56);

function compare($a, $b) {
    echo "Comparing $a to $b\n";
    if ($a == $b) return 0;
    return ($a < $b) ? -1 : 1;
}

usort($myArray,"compare");
print_r($myArray);
?>

输出

vinko@mithril:~$ php sort.php
Comparing 18 to 19
Comparing 56 to 18
Comparing 12 to 18
Comparing 1 to 18
Comparing 12 to 1
Comparing 56 to 19
Array
(
    [0] => 1
    [1] => 12
    [2] => 18
    [3] => 19
    [4] => 56
)

从输出和源代码可以看出,使用的排序算法确实是快速排序实现,可以在PHP源代码中的Zend/zend_qsort.c中查看(链接的版本有点旧,但大体相同)。

它从数组的中间选择枢轴值,即18,然后需要重新排列列表,使得所有小于(根据使用的比较函数)枢轴值的元素都在枢轴值之前,而所有大于枢轴值的元素都在其后面,我们可以在它首先将所有元素与18进行比较时看到它正在执行此操作。

进一步的图示说明:

步骤0:(1,19,18,12,56); //枢轴值:18
步骤1:(1,12,18,19,56); //第一次重新排序后
步骤2a:(1,12);         //对较小的部分做同样的递归操作,这里是以12为枢轴值,如果检查输出结果,就会发现下一个被比较的值是12。
步骤2b:(19,56);        //对较大的部分做同样的操作

非常好的回答。Paul的回答足够并且第一个给出。因此我选择了接受他的回答。虽然如此,我已经为你点赞并且感谢你的认真回答。 - Sampson
9
为了举例说明,我建议第一次回答并不总是最好的。如果第二个答案更加完整,人们应该因为花费时间来充分回答问题而得到奖励。 - acrosman

6
为了对任何东西进行排序,您需要一种比较两个项目并确定哪一个在另一个之前的方法。这就是您提供给usort的内容。此函数将从输入数组中传递两个项目,并返回它们应该处于的顺序。
一旦您有了比较两个元素的方法,您可以使用您选择的排序算法。
如果您不熟悉,您可能想查看像bubblesort这样的简单天真算法如何使用比较函数。
在幕后,PHP正在使用quicksort

2
我相信Jonathan对“幕后”的部分很感兴趣。 - Ionuț G. Stan

0

usort()或uasort()在排序结果上存在一个人性化的错误。请参见以下代码段:

function xxx($a,$b) { if ($a==$b) return 0; else return $a<$b?-1:1; }
$x=array(1=>10,2=>9,3=>9,4=>9,5=>6,6=>38);
uasort($x,'xxx');
print_r($x);

结果是:

Array ( [5] => 6 [4] => 9 [3] => 9 [2] => 9 [1] => 10 [6] => 38 )

你看到这个错误了吗?没有?好的,让我解释一下。 原来的三个'9'元素按键顺序是:2、3、4。但在结果中,这三个'9'元素现在按键顺序是:4、3、2,即相等值元素在排序后按相反的键顺序排列。

如果元素只有单个值,就像上面的例子一样,我们可以接受。然而,如果元素是复合值,那么它可能会导致人类感知的错误。这里有另外一段代码片段。我们要水平排序许多点,也就是根据x坐标值的升序进行排序:

function xxx($a,$b) { if ($a['x']==$b['x']) return 0; else return $a['x']<$b['x']?-1:1; }
$x=array(1=>array('x'=>1, 'v'=>'l'),2=>array('x'=>9, 'v'=>'love'),
       3=>array('x'=>9,  'v'=>'Lara'),4=>array('x'=>9,  'v'=>'Croft'),
       5=>array('x'=>15,  'v'=>'and'),6=>array('x'=>38,  'v'=>'Tombraider'));
uasort($x,'xxx');
print_r($x);

结果是:

Array ( [1] => Array ( [x] => 1 [v] => l ) [4] => Array ( [x] => 9 [v] => croft ) 
             [3] => Array ( [x] => 9 [v] => Lara ) [2] => Array ( [x] => 9 [v] => love )
             [5] => Array ( [x] => 15 [v] => and ) [6] => Array ( [x] => 38 [v] => Tombraider ) )

您看到 '我爱Lara Croft和Tombraider' 变成了 '我Croft Lara爱和Tombraider'。

我称之为人性化bug,因为它取决于您使用的情况以及在比较值相同时您认为该如何在现实世界中进行排序。


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