Почему работает usort (php), даже если не возвращаются целые числа? - PullRequest
2 голосов
/ 25 января 2020

Вчера на работе я наткнулся на кусок кода, который был примерно такой:

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

это перевернуло пару переключателей в моей голове, поскольку это может вернуть только логическое значение true или false вместо 0 1 или -1, что четко указано в php. net. В лучшем случае PHP будет интерпретировать false как 0 и true как 1. Поэтому один (коллега, который написал это) может утверждать, что это действительно работает.

Я сделал аргумент, что это не может работать, потому что это будет никогда не вернуть -1. В функции return-либо-true-or-false отсутствует обратный вызов сортировки, и, как я думаю, вы получите либо неверный результат, либо процесс не такой производительный.

Я на самом деле приступил к написанию пары тестов, и хотя я не являюсь экспертом в сортировке, я фактически дошел до того, что вы фактически можете покончить с состоянием -1 и все же получить правильную сортировку с тем же количеством вычислений

фактически вы могли бы заменить:

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

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

на

return $a > $b;

Теперь, как я уже сказал, я ни в коем случае не эксперт по сортировке, и мои тесты были менее тщательными. Однако мои результаты с маленькими и большими массивами показали, что это правда. Массив всегда сортировался правильно, количество вычислений было одинаковым при использовании любого из методов

Так что на самом деле не так уж важно, что третьего состояния (-1)

* 1019 не существует * Я все еще придерживаюсь своего мнения, что вы должны следовать документам, особенно PHP, к письму. Когда php говорит, что мне нужно 0, отрицательное или положительное число, то это именно то, что нужно делать. Вы не должны оставлять такие вещи на волю случая и смотреть, как все работает случайно.

Однако я заинтригован всем этим и хотел бы получить представление от кого-то, кто знает больше

мыслей? кто-нибудь?

Ответы [ 3 ]

2 голосов
/ 25 января 2020

Вам не нужно возвращать все -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 по порядку

1 голос
/ 25 января 2020

A для сортировки сравнения требуется только полный предварительный порядок, который в основном является отношением "меньше или равно". Другими словами, не имеет значения, является ли 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]

, что противоречит тому, что говорит функция.

0 голосов
/ 25 января 2020

Лог c в вашем uasort() имеет лог c, который позволяет выполнять меньшее общее сравнение и, следовательно, (при условии, что правила сортировки дают ожидаемый результат), выполняется с более высокой эффективностью.

PHP s uasort() использует Quicksort , насколько я могу судить, но в документах говорится, что вы не должны полагаться на этот факт (возможно, в случае, если он изменится в будущем). SO reference .

Вот demo , который показывает, что вызов сортировки выполняет только достаточно сравнений, чтобы потенциально разорвать все связи. Использование трехстороннего сравнения оказывается более дорогостоящим, поскольку требуется больше сравнений, но, скорее всего, из-за инструкций сортировки будет получен другой тип сортировки.

$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({$a['id']} -vs- {$b['id']}) " , ($a['sort_code'] ?? 'undeclared') , " -vs- " , ($b['sort_code'] ?? 'undeclared') , " eval: ";
    echo $eval = (int)(isset($a['sort_code']) && isset($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({$a['id']} -vs- {$b['id']}) " , ($a['sort_code'] ?? 'undeclared') , " -vs- " , ($b['sort_code'] ?? 'undeclared') , " eval: ";
    echo $eval = (int)(($a['sort_code'] ?? 0) <=> ($b['sort_code'] ?? 0));
    return $eval;
});
echo "\n---\n";
var_export($array);

Вывод:

(1 -vs- 2) 4 -vs- 2 eval: 1
(1 -vs- 3) 4 -vs- undeclared eval: 0
(3 -vs- 4) undeclared -vs- 8 eval: 0
(4 -vs- 5) 8 -vs- undeclared eval: 0
(5 -vs- 6) undeclared -vs- 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,
  ),
)
======

(2 -vs- 1) 2 -vs- 4 eval: -1
(1 -vs- 3) 4 -vs- undeclared eval: 1
(2 -vs- 3) 2 -vs- undeclared eval: 1
(1 -vs- 4) 4 -vs- 8 eval: -1
(4 -vs- 5) 8 -vs- undeclared eval: 1
(1 -vs- 5) 4 -vs- undeclared eval: 1
(2 -vs- 5) 2 -vs- undeclared eval: 1
(3 -vs- 5) undeclared -vs- undeclared eval: 0
(4 -vs- 6) 8 -vs- 6 eval: 1
(1 -vs- 6) 4 -vs- 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,
  ),
)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...