【问题标题】:Why does SORT_REGULAR produce inconsistent results in PHP?为什么 SORT_REGULAR 在 PHP 中会产生不一致的结果?
【发布时间】:2013-12-26 22:22:05
【问题描述】:

我正在研究一个使 PHP 中的数组排序更容易的类,并且我一直在使用 SORT_ 常量,但是行为或 SORT_REGULAR(默认排序类型)似乎根据您添加的顺序而有所不同数组中的项目。此外,我无法发现为什么会出现这种情况的模式。

数组项:

$a = '0.3';
$b = '.5';
$c = '4';
$d = 'F';
$e = 'z';
$f = 4;

场景 1:

sort(array($d, $e, $a, $f, $b, $c));

// Produces...
array(6) {
  [0]=>
  string(3) "0.3"
  [1]=>
  string(2) ".5"
  [2]=>
  string(1) "4"
  [3]=>
  string(1) "F"
  [4]=>
  string(1) "z"
  [5]=>
  int(4)
}

场景 2:

sort(array($d, $e, $b, $f, $c, $a));

// Produces...
array(6) {
  [0]=>
  string(3) "0.3"
  [1]=>
  string(2) ".5"
  [2]=>
  string(1) "F"
  [3]=>
  string(1) "z"
  [4]=>
  int(4)
  [5]=>
  string(1) "4"
}

有什么想法吗?

【问题讨论】:

  • @hakre:这是关于用于给定排序策略的比较函数/运算符的传递性,而不是关于稳定性

标签: php arrays sorting


【解决方案1】:

http://php.net/sort

警告

对具有混合类型值的数组进行排序时要小心,因为 sort() 会产生不可预知的结果。

您应该使用 SORT_* 常量之一。

这里有几个cmets:

【讨论】:

  • 谢谢,我实际上正在编写一个单元测试,以确保排序类复制原生 PHP 算法,这就是我偶然发现问题的方式。每次我更改输入时,预期输出的顺序都会改变!我会把它归结为“其中一件事”。
【解决方案2】:

这种行为是“预期的”(或至少是已知的),因为您对值使用不同的类型(字符串和整数)。参见sort()函数的手册。

警告

对具有混合类型值的数组进行排序时要小心,因为 sort() 会产生不可预知的结果。

最有可能的是,在排序算法的某个时刻,它将两个值作为整数而不是字符串进行比较。为避免这种情况,请勿尝试对具有不同类型的数组进行排序(如手册所述)。

【讨论】:

  • 谢谢...有趣的解释。我不太了解内部结构,但我猜一切都会与其他一切进行比较,因此无论输入顺序如何,您都会期望得到一致的结果,但显然不是这样。
【解决方案3】:

SORT_REGULAR 类似于<

似乎SORT_REGULAR 遵循与< 运算符相同的规则(因此,> 运算符)。

在我自己的测试中,对于任何两个值 $v0 和 $v1,以下断言确实通过了:

$pair = [$v0, $v1];
sort($pair);
assert($pair[0] < $pair[1]);

对于混合类型,&lt; 不是严格的弱序。

不幸的是,&lt; 与字符串和整数的混合具有循环行为,并且不具有传递性。因此,它不是Strict weak order

这可以通过以下断言来证明,pass

assert('3' < '10');    // Numeric comparison. 2 < 12.
assert('10' < '2 ');  // Lexicographical comparison
// Circular:
assert('2 ' < '3');   // Lexicographical comparison
// Not transitive:
assert(!('3' < '2 '));   // Lexicographical comparison

// And just because it's interesting:
assert('2 ' < 3);    // Numeric comparison. 2 < 3.

排序的思想是,在排序好的item列表中,如果$i &lt; $j,那么!($sorted_items[$j] &lt; $sorted_items[$i])

这只有在&lt; 是严格的全序或严格的弱序时才有可能。

有时,&lt; 将字符串读取为数字。

以下规则似乎适用于&lt;

  • 如果两个值都是“看起来像”(*) 数字的字符串,则应用数字比较。
  • 如果一个值一个数字,则应用数字比较。
  • 如果两个值都是字符串,并且其中至少有一个看起来不像数字,则应用字母(字典)比较。

(*) 字符串 '4' 确实“看起来像”数字 4。字符串 '4 x' 并不“看起来像”数字 4,但 (int)'4 x' 的计算结果仍为 4。

原始示例

这个具体示例中的排序实际上在 PHP 7 中是一致的,只是在 PHP 5.* 中不同,可以看到 here

这并不意味着 PHP 7 对这个问题免疫。在这个特定的例子中,它恰好没问题。

比较各个值reveals

hhvm-3.9.1 - 3.12.0, 7.0.0 - 7.1.0alpha2

'0.3' < '.5'
'0.3' < '4'
'0.3' < 'F'
'0.3' < 'z'
'0.3' < 4
'.5' < '4'
'.5' < 'F'
'.5' < 'z'
'.5' < 4
'4' < 'F'
'4' < 'z'
'4' < 4
'F' < 'z'
'F' < 4
'z' < 4

5.5.0 - 5.6.23

'0.3' < '.5'
'0.3' < '4'
'0.3' < 'F'
'0.3' < 'z'
'0.3' < 4
'.5' < '4'
'.5' < 'F'
'.5' < 'z'
'.5' < 4
'4' < 'F'
'4' < 'z'
4 < '4'
'F' < 'z'
'F' < 4
'z' < 4

现在让我们删除无聊的部分。

  • “0.3”比其他所有内容都小,因此不属于任何圆圈。
  • “.5”比其他所有内容都小(删除“0.3”后),因此不属于任何圆圈。

在 PHP 7 中:

  • '4' 比其他所有元素都小,因此不属于任何圆圈。
  • 4 比其他任何东西都大,因此不属于任何圈子。
  • 'F' &lt; 'z' 仍然存在。没有什么循环的。因此,只有一种可能的排序顺序:['0.3', '.5', '4', 'F', 'z', 4]

在 PHP 5.* 中:

有多种方法可以用这些结果建立圈子。

这样一个圈子是:'4' &lt; 'F''F' &lt; 44 &lt; '4'

这意味着:在任何排序顺序中,至少有两个位置 $i$j$i &lt; $j$sorted[$j] &lt; $sorted[$i]

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-01-25
    • 2023-01-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-18
    相关资源
    最近更新 更多