Как выйти из итерационных функций массива (array_reduce) в PHP
У меня есть функция array_reduce, которую я готов выйти, когда будут выполнены определенные критерии.
$result = array_reduce($input, function($carrier, $item) {
// do the $carrier stuff
if (/* god was one of us */) {
break; //some break analogue
}
return $carrier;
});
Как мне этого добиться? Или я должен использовать foreach вместо этого?
5 ответов
array_reduce
используется для написания кода функционального стиля, который всегда перебирает весь массив. Вы можете либо переписать, чтобы использовать обычный цикл foreach для реализации логики короткого замыкания, либо просто вернуть текущий $carrier
неизмененной. Это все равно будет повторяться по вашему полному массиву, но это не изменит результат (как вы сказали, это больше похоже на continue
)
По аналогичному ответу.
Отключить array_walk от анонимной функции
Лучший и худший способ завершить это - Исключение. Не рекомендую этот способ, но это решение вашего вопроса:
try {
$result = array_reduce( $input, function ( $carrier, $item ) {
// do the $carrier stuff
$condition = true;
if ( $condition ) {
throw new Exception;
}
return $carrier;
} );
} catch ( Exception $exception ) {
echo 'Break';
}
Как бы я решил проблему
Я бы создал глобальную функцию или написал расширение PHP и добавил бы функцию
Есть хороший ответ о написании расширения PHP:Как сделать расширение PHP
array_reduce_2();
Но есть проблема с нарушением реализации. Необходимо определить, какое состояние не работает. Ниже реализации проверяет, вернулся ли обратный вызов. Если так - выход из строя.
Этот способ позволяет проверить, прервалось ли выполнение, проверив тип возвращаемого значения.
реализация array_reduce_2
function array_reduce_2( $array, $callback, $initial = null ) {
$len = count( $array );
$index = 0;
if ( $len == 0 && count( func_get_args() ) == 1 ) {
return null;
}
$result = $initial ?? $array[ $index ++ ];
for ( ; $index < $len; $index ++ ) {
$result = $callback( $result, $array[ $index ] );
if ( ! is_callable( $result ) ) {
continue;
}
// break;
return $result;
}
return $result;
Я использовал идею реализации JS и соответствующим образом переписал для PHP. https://gist.github.com/keeto/229931
Обнаружение, если произошел обрыв
$input = [ 'test', 'array', 'god was one of us' ];
$result = array_reduce_2( $input, function ( $carrier, $item ) {
// do the $carrier stuff
if ( $item === 'god was one of us' ) {
return function () {
return 'god was one of us';
};
}
return $carrier;
} );
$is_break = is_callable( $result );
if ( $is_break ) {
echo $result();
exit;
}
Важно отметить!
Этот
array_reduce_2
реализация работает правильно, только если вам не нужно возвращать нормальное значение в качестве обратного вызова.
Во-первых, позвольте мне сказать, что array_reduce, вероятно, мои любимые функции - я известен (ну, в очень маленьком кругу) тем, что взял 40 строк четко написанного кода и заменил их четырьмя более сложными 10-строчными вызовами array_reduce для выполнения то же самое!
К сожалению, функции массива PHP, похоже, обязательно захотят выполнить свою задачу. Это, в сочетании с невозможностью создать рекурсивную безымянную функцию, затрудняет решение этой общей ситуации. Не желая помещать в свой код много уродливых циклов for, я стараюсь похоронить их в другой функции (см. Сокращение ниже), как это было на предыдущем плакате.
Стоит отметить, что это никоим образом не так эффективно, как использование функций массива, и в большинстве случаев лучше просто позволить функции сокращения массива использовать флаг «готово» для быстрого просмотра ненужных значений. Во всяком случае, это что-то вроде array_reduce (функция оценки, использующая нулевой возврат, чтобы указать, что она закончена). Цель состоит в том, чтобы сложить числа в массиве, пока вы не дойдете до 4.
<?php
$init = 0;
$arr = [1,2,3,4,5,6,7,8,9,0];
$func = function($c, $it) {
if ($it == 4) return null;
return $c + $it;
};
function reduce($arr, $f, $init) {
for ($c = $init; count($arr); ) {
$newc = $f($c, array_shift($arr));
if (!isset($newc)) break;
$c = $newc;
}
return $c;
}
echo reduce($arr, $func, $init) . "\n"; // 6
Я предлагаю использовать
foreach
петли вместо этого. Причины не использовать
array_reduce
находятся:
Звуковые причины:
- Тип не проверяется статически. Таким образом, проверки кода не показывают ошибок типа, если они есть во входных аргументах или аргументах обратного вызова.
- Он возвращается
mixed
, поэтому проверки не выявляют ошибок, если вы неправильно используете результат, или они могут показать ложное срабатывание, если вы используете его правильно. - Вы не можете сломаться.
Мнения причин:
- Это тяжелее для глаз. Иметь
$result
и добавление к нему в цикле (или что бы вы ни делали) легче читать, чем понять, что что-тоreturn
Эд, а затем прошел как$carry
аккумулятор в следующем вызове. - Мне лень правильно извлекать функции. Если я извлеку одну операцию для обратного вызова, я могу найти код достаточно коротким, чтобы не извлекать всю операцию с массивом в функцию, что действительно должно быть выполнено в первую очередь.
- Если вы используете условие для прерывания, есть большая вероятность, что однажды вам могут понадобиться другие аргументы для этой функции обратного вызова. При фиксированной сигнатуре обратного вызова вам придется передавать аргументы с помощью
use
ключевое слово, которое на самом деле намного сложнее читать, чем не обратное.
breakable_array_reduce()
function breakable_array_reduce(array $array, callable $callback, $initial = null) {
$result = $initial;
foreach ($array as $value) {
$ret = $callback($result, $value);
if (false === $ret) {
return $result;
} else {
$result = $ret;
}
}
return $result;
}
использование
// array of 10 values
$arr = [
1,
1,
1,
1,
1,
1,
1,
1,
1,
1
];
// callback function which stops once value >= 5
$sum_until_five = function($initial, $value) {
if ($initial >= 5) {
return false;
} else {
return $initial + $value;
}
};
// calculate sum of $arr using $sum_until_five()
$sum = breakable_array_reduce($arr, $sum_until_five);
// output: 5
echo $sum;
Объяснение
breakable_array_reduce()
будет работать так же, как
array_reduce()
если / до обратного вызова
$callback
возвращается
bool(false)
Альтернативная реализация с использованием ключей массива:
breakable_array_reduce_keyed ()
function breakable_array_reduce_keyed(array $array, callable $callback, $initial = null) {
$result = $initial;
foreach ($array as $key => $value) {
$ret = $callback($result, $value, $key);
if (false === $ret) {
return $result;
} else {
$result = $ret;
}
}
return $result;
}
использование
// array of values
$arr = [
'foo' => 1,
'bar' => 1,
'baz' => 1
];
// callback function which stops when $key === 'baz'
$sum_until_baz = function($initial, $value, $key) {
if ('baz' === $key) {
return false;
} else {
return $initial + $value;
}
};
// calculate sum of $arr using $sum_until_baz()
$sum = breakable_array_reduce($arr, $sum_until_baz);
// output: 2
echo $sum;
PS Я только что написал и полностью протестировал это локально.