万千封印
稳定的排序假设你有一个像这样的数组:['Kale', 'Kaleidoscope', 'Aardvark', 'Apple', 'Leicester', 'Lovely']而现在你只想对第一个字母进行排序:usort($array, function($a, $b) {
return strcmp($a[0], $b[0]);});结果如下:['Apple', 'Aardvark', 'Kale', 'Kaleidoscope', 'Lovely', 'Leicester']那种不稳定!敏锐的观察者可能已经注意到,数组排序算法(QuickSort)没有产生稳定的结果,并且没有保留相同第一个字母的单词之间的原始顺序。这个案例很简单,我们应该比较整个字符串,但是我们假设你的用例更复杂,比如不同字段上的两个连续排序不应该抵消彼此的工作。施瓦茨变换Schwartzian变换,也称为decorate-sort-undecorate习语,使用固有的不稳定排序算法实现稳定排序。首先,使用包含主键(值)和辅助键(其索引或位置)的另一个数组来装饰每个数组元素:array_walk($array, function(&$element, $index) {
$element = array($element, $index); // decorate});这会将数组转换为:[
['Kale', 0], ['Kaleidoscope', 1],
['Aardvark', 2], ['Apple', 3],
['Leicester', 4], ['Lovely', 5]]现在,我们调整比较步骤; 我们再次比较第一个字母,但如果它们相同,则使用二级密钥保留原始排序:usort($array, function($a, $b) {
// $a[0] and $b[0] contain the primary sort key
// $a[1] and $b[1] contain the secondary sort key
$tmp = strcmp($a[0][0], $b[0][0]);
if ($tmp != 0) {
return $tmp; // use primary key comparison results
}
return $a[1] - $b[1]; // use secondary key});之后,我们不合理:array_walk($array, function(&$element) {
$element = $element[0];});最终结果:['Aardvark', 'Apple', 'Kale', 'Kaleidoscope', 'Leicester', 'Lovely']重用怎么样?您必须重写比较函数才能使用转换后的数组元素; 您可能不想编辑精细的比较函数,因此这里是比较函数的包装器:function stablecmp($fn){
return function($a, $b) use ($fn) {
if (($tmp = call_user_func($fn, $a[0], $b[0])) != 0) {
return $tmp;
} else {
return $a[1] - $b[1];
}
};}让我们使用这个函数编写排序步骤:usort($array, stablecmp(function($a, $b) {
return strcmp($a[0], $b[0]);}));瞧!您的原始比较代码又回来了。