【问题标题】:How come usort (php) works even when not returning integers?即使不返回整数,usort(php)如何工作?
【发布时间】:2020-05-11 11:23:22
【问题描述】:

昨天在工作中偶然发现了一段代码,大致是这样的:

uasort($array, function($a, $b) {
    return isset($a['sort_code']) && isset($b['sort_code']) && $a['sort_code'] > $b['sort_code'];
});

这在我的脑海中翻转了几个开关,因为这只能返回布尔值 true 或 false,而不是 php.net 中明确说明的 0 1 或 -1。最佳情况下,PHP 会将 false 解释为 0,将 true 解释为 1。所以一个人(写这篇文章的同事)可能会争辩说它确实有效。

我认为它不可能工作,因为它永远不会返回 -1。排序回调中的此 return-either-true-or-false 中缺少一个状态,我认为这是您将得到错误结果或过程性能不佳的方式。

我实际上开始编写几个测试,虽然我不是排序方面的专家,但我实际上已经达到了你可以真正取消 -1 状态并仍然通过相同数量的计算得到正确排序的地步

实际上你可以替换:

if ($a === $b) {
    return 0;
}

return $a < $b ? -1 : 1; 

return $a > $b;

现在,正如我所说,我绝不是排序方面的专家,我的测试也不够彻底。然而,我对小型和大型阵列的结果表明这是真的。使用任何一种方法,数组总是得到正确排序,计算量相同

所以说没有第三态(-1)似乎真的没什么大不了的

我仍然坚持我的观点,即您应该严格遵守文档,尤其是 PHP。当 php 说我需要 0、负数或正数时,这正是我们应该做的。你不应该把这样的事情留给偶然的机会,看看事情是如何运作的。

但是我对这一切很感兴趣,希望了解更多的人提供一些见解

想法?有人吗?

【问题讨论】:

  • 你总是可以使用宇宙飞船运算符 - stackoverflow.com/questions/30365346/…
  • 我相信我已经偶然发现了一些不遵循规范提供错误结果的情况,但我现在找不到。在任何情况下,如果 PHP 团队决定在后台调整排序算法,如果不遵循手册,您将面临代码在未来版本中不再正确排序的风险。
  • Same question for Javascript。简而言之:这完全取决于其背后的算法。出于上述原因,我会遵循规范。

标签: php sorting usort php-internals


【解决方案1】:

您不必实际返回所有 -1、0 和 1 以使函数正常工作。返回 0 不会就地切换元素。您也可以返回 -1345 之类的内容来充当 -1。

仅返回 0 或 1 意味着您只想将一些元素“下沉”到数组的末尾,例如在数组的末尾或开头有所有 NULL 值,其他排序并不重要:

$input = ['c', 'a', null, 'b', null];

usort($input, function ($a, $b) {
    return !is_null($b);
});

// 'b', 'a', 'c', null, null
var_dump($input);

在您的情况下,它会按顺序排序到没有sort_code 的元素以及sort_code 的元素

【讨论】:

  • 我(有点)明白你的意思。但是:即使在几十年前编写的最糟糕的单体应用中,也不太可能出现包含不同类型值的数组的情况。即便如此,我仍然认为仅仅为了整理东西而编写 return !is_null(...) 只是一种糟糕的做法。在最坏的情况下,您仍然应该只从该回调中返回整数。一路向下发送空值: if (null === $b) { return 1; }
  • 或者您可以利用 PHP 中的丢失类型和类型转换来编写更少的仍然有效的代码
【解决方案2】:

comparison sort 只需要一个总预购,这基本上是一个“小于或等于”的关系。换句话说,a

但是,要正常工作,该函数需要是可传递的(否则关系将不是全预序)。这不是原始帖子中给出的功能的情况。

这是一个例子:

$array = [
    ['sort_code' => 2],
    [],
    ['sort_code' => 1]
];

uasort($array, function($a, $b) {
    return isset($a['sort_code']) && isset($b['sort_code']) && $a['sort_code'] > $b['sort_code'];
});

var_export($array);

给出:

array (
  0 => 
  array (
    'sort_code' => 2,
  ),
  1 => 
  array (
  ),
  2 => 
  array (
    'sort_code' => 1,
  ),
)

sort_code 2 出现在 1 之前,这不是有意的。那是因为,根据比较函数:

$array[0] <= $array[1]
$array[1] <= $array[2]
$array[0] >  $array[2]

如果关系是传递的,前两行将暗示:

$array[0] <= $array[2]

这与函数所说的相矛盾。

【讨论】:

    【解决方案3】:

    uasort() 中的逻辑可以满足减少整体比较的需求,因此(假设排序规则提供预期结果)执行效率更高。

    据我所知,PHP 的 uasort() 使用 Quicksort,但文档说你不应该依赖这个事实(也许将来会发生变化)。一个SO reference

    这是一个demo,它表明 sort 调用只进行了足够的比较,可能会破坏所有的关系。使用三向比较最终会更加昂贵,因为需要进行更多比较,但由于排序说明,可能会提供不同(和更好)的排序。

    $array = [
        ['id' => 1, 'sort_code' => 4],
        ['id' => 2, 'sort_code' => 2],
        ['id' => 3],
        ['id' => 4, 'sort_code' => 8],
        ['id' => 5],
        ['id' => 6, 'sort_code' => 6]
    ];
    
    uasort($array, function($a, $b) {
        echo "\n" . json_encode($a) . " -vs- " . json_encode($b) . " eval: ";
        echo $eval = (int)(isset($a['sort_code'], $b['sort_code']) && $a['sort_code'] > $b['sort_code']);
        return $eval;
    });
    echo "\n---\n";
    var_export($array);
    
    echo "\n======\n";
    
    uasort($array, function($a, $b) {
        echo "\n" . json_encode($a) . " -vs- " . json_encode($b) . " eval: ";
        echo $eval = ($a['sort_code'] ?? 0) <=> ($b['sort_code'] ?? 0);
        return $eval;
    });
    echo "\n---\n";
    var_export($array);
    

    输出:

    {"id":1,"sort_code":4} -vs- {"id":2,"sort_code":2} eval: 1
    {"id":1,"sort_code":4} -vs- {"id":3} eval: 0
    {"id":3} -vs- {"id":4,"sort_code":8} eval: 0
    {"id":4,"sort_code":8} -vs- {"id":5} eval: 0
    {"id":5} -vs- {"id":6,"sort_code":6} eval: 0
    ---
    array (
      1 => 
      array (
        'id' => 2,
        'sort_code' => 2,
      ),
      0 => 
      array (
        'id' => 1,
        'sort_code' => 4,
      ),
      2 => 
      array (
        'id' => 3,
      ),
      3 => 
      array (
        'id' => 4,
        'sort_code' => 8,
      ),
      4 => 
      array (
       'id' => 5,
      ),
      5 => 
      array (
        'id' => 6,
        'sort_code' => 6,
      ),
    )
    ======
    
    {"id":2,"sort_code":2} -vs- {"id":1,"sort_code":4} eval: -1
    {"id":1,"sort_code":4} -vs- {"id":3} eval: 1
    {"id":2,"sort_code":2} -vs- {"id":3} eval: 1
    {"id":1,"sort_code":4} -vs- {"id":4,"sort_code":8} eval: -1
    {"id":4,"sort_code":8} -vs- {"id":5} eval: 1
    {"id":1,"sort_code":4} -vs- {"id":5} eval: 1
    {"id":2,"sort_code":2} -vs- {"id":5} eval: 1
    {"id":3} -vs- {"id":5} eval: 0
    {"id":4,"sort_code":8} -vs- {"id":6,"sort_code":6} eval: 1
    {"id":1,"sort_code":4} -vs- {"id":6,"sort_code":6} eval: -1
    ---
    array (
      2 => 
      array (
        'id' => 3,
      ),
      4 => 
      array (
        'id' => 5,
      ),
      1 => 
      array (
        'id' => 2,
        'sort_code' => 2,
      ),
      0 => 
      array (
        'id' => 1,
        'sort_code' => 4,
      ),
      5 => 
      array (
        'id' => 6,
        'sort_code' => 6,
      ),
      3 => 
      array (
        'id' => 4,
        'sort_code' => 8,
      ),
    )
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-12-07
      • 2017-09-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-08-16
      • 1970-01-01
      相关资源
      最近更新 更多