массивы — PHP: array_uintersect () непредвиденное поведение

Тестовый скрипт

$i = 0;
array_uintersect(['foo', 'bar'], ['baz', 'qux'], function($a, $b) use (&$i) {
print_r([$a, $b, $i++]);
});

Фактический результат

Array
(
[0] => bar
[1] => foo
[2] => 0
)
Array
(
[0] => qux
[1] => baz
[2] => 1
)
Array
(
[0] => bar
[1] => qux
[2] => 2
)
Array
(
[0] => bar
[1] => foo
[2] => 3
)

ожидаемый результат

Array
(
[0] => foo
[1] => baz
[2] => 0
)
Array
(
[0] => bar
[1] => qux
[2] => 1
)

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

Кроме того, я ожидаю, что та же логика будет применяться, если я передам дополнительный массив array_uintersect — еще один аргумент передается обратному вызову ($c, например).

Может кто-нибудь объяснить это поведение?

3

Решение

Что не упоминается в array_uintersect документы это то, что внутри PHP виды все массивы во-первых, слева направо. Только после Сортировка массивов позволяет ли PHP пройти их (снова слева направо), чтобы найти пересечение.

Третий аргумент (функция сравнения) передается внутреннему алгоритму сортировки, а не пересекающемуся алгоритму. Таким образом, видимый результат отладки является алгоритмом сортировки, определяющим порядок.

реализация zend_sort как правило, использует использует быструю реализацию быстрой сортировки. Для массивов размера в вашем примере PHP использует сортировку вставкой. Для больших массивов PHP использует 3 или 5 точечные точки, чтобы улучшить сложность наихудшего случая.

Поскольку вы не возвращаете явно значение из функции сравнения, PHP по умолчанию возвращает null (0), и, поскольку PHP использует сортировку вставкой, вы наблюдаете поведение O (n * n), поскольку сортировка обходит все комбинации.

1

Другие решения

Я понятия не имею, почему вы ожидаете ничего от обратного вызова сравнения, кроме сравнения значений массивов. Единственная цель обратного вызова — сравнить следующую пару элементов из обоих массивов.

Функция возвращает результат пересечения двух массивов. В обратном вызове вы выражаете свое представление о том, как значения должны сравниваться. Например, следующий код предполагает, что пересечение должно быть выполнено путем сравнения первых символов строк:

$a = array_uintersect(['foo', 'bar'], ['baz', 'qux'], function($a, $b) {
return strcmp($a[0], $b[0]);
});

print_r($a);

Выход

Array
(
[1] => bar
)

Порядок элементов, передаваемых в обратный вызов, определяется внутренними компонентами PHP и может легко измениться в будущем.

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

2

Я считаю, что первые два вызова используются для заполнения переменных во внутреннем алгоритме. Но поскольку вы не возвращаете ничего, что алгоритм может использовать для определения равенства / сортировки, он запускает только следующие два.

Если вы действительно вернетесь 0, 1 или же -1 тогда вы увидите полную цепочку сравнения, которая необходима для вычисления пересечения:

$i = 0;
array_uintersect(['foo', 'bar'], ['baz', 'qux'], function($a, $b) use (&$i) {
print_r([$a, $b, $i++]);

if ($a === $b) return 0;
if ($a  >  $b) return 1;
return -1;
});

Урожайность:

Array
(
[0] => bar
[1] => foo
[2] => 0
)
Array
(
[0] => qux
[1] => baz
[2] => 1
)
Array
(
[0] => bar
[1] => baz
[2] => 2
)
Array
(
[0] => foo
[1] => baz
[2] => 3
)
Array
(
[0] => foo
[1] => baz
[2] => 4
)
Array
(
[0] => foo
[1] => qux
[2] => 5
)
1

Я думаю, что вы ищете это;)

$result = array_map(function($a, $b) {
return [$a, $b];
}, ['foo', 'bar'], ['baz', 'qux']);
var_dump($result);

Это будет выводить

array(2) {
[0]=>
array(2) {
[0]=>
string(3) "foo"[1]=>
string(3) "baz"}
[1]=>
array(2) {
[0]=>
string(3) "bar"[1]=>
string(3) "qux"}
}

Обновление: возвращает желаемый результат с array_uintersect метод. Это не самый эффективный способ сделать это и не тестировать его с разными наборами данных и т. Д., Но он должен работать.

$entities = [
[
'id' => 1,
'timestamp' => 1234
],
[
'id' => 2,
'timestamp' => 12345
],
[
'id' => 3,
'timestamp' => 123456
],
[
'id' => 8,
'timestamp' => 123456
],
[
'id' => 10,
'timestamp' => 123456
],
[
'id' => 11,
'timestamp' => 123456
],
[
'id' => 12,
'timestamp' => 123456
]
];

$identities = [1, 11, 2, 8, 10];

$result = array_uintersect($entities, $identities, function($a, $b) {

// Both array skip
if (is_array($a) && is_array($b)) {
if ($a['id'] > $b['id']) {
return 1;
}
return -1;
}

// Both int skip
if (is_int($a) && is_int($b)) {
if ($a > $b) {
return 1;
}
return -1;
}

// $a is array
if (is_array($a)) {
if ($a['id'] == $b) {
return 0;
}
elseif ($a['id'] > $b) {
return 1;
}
return -1;
}

// $b is array
if($b['id'] == $a) {
return 0;
}
if($a > $b['id']) {
return 1;
}

return -1;
});
var_dump($result);

и результат

array(5) {
[0]=>
array(2) {
["id"]=>
int(1)
["timestamp"]=>
int(1234)
}
[1]=>
array(2) {
["id"]=>
int(2)
["timestamp"]=>
int(12345)
}
[3]=>
array(2) {
["id"]=>
int(8)
["timestamp"]=>
int(123456)
}
[4]=>
array(2) {
["id"]=>
int(10)
["timestamp"]=>
int(123456)
}
[5]=>
array(2) {
["id"]=>
int(11)
["timestamp"]=>
int(123456)
}
}
0
<?php
$i  = 0;
$r1 = ['foo', 'bar'];
$r2 = ['baz', 'qux'];
$result = array_uintersect($r1, $r2, function($a, $b){
return ($a[0]> $b[0]);
});var_dump($result);
// YIELDS::
array (size=2)
0 => string 'foo' (length=3)
1 => string 'bar' (length=3)
-4
По вопросам рекламы [email protected]