我有一个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吗?
答案 0 :(得分:2)
首先,让我说 array_reduce 可能是我最喜欢的函数之一——我很出名(嗯,在一个很小的圈子里)把 40 行清晰的代码替换为 4 行更难理解的 10 行array_reduce 调用来做同样的事情!
遗憾的是,PHP 数组函数似乎注定要完成它们的任务。再加上无法创建递归未命名函数,使得这种常见情况难以处理。不想在我的代码中放置很多丑陋的 for 循环,我倾向于将它们埋在另一个函数中(参见下面的 reduce),就像之前的海报一样。
值得指出的是,这并不像使用数组函数那样有效,而且在大多数情况下,最好让数组 reduce 函数使用“完成”标志来快速遍历不需要的值。无论如何,这与 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
答案 1 :(得分:1)
length(n)
用于编写始终在整个数组上迭代的功能样式的代码。您可以重写以使用常规的foreach循环来实现短路逻辑,也可以仅返回未修改的当前array_reduce
。这仍然会遍历整个数组,但不会改变结果(如您所说,这与$carrier
更为相似)
答案 2 :(得分:1)
Break array_walk from anonymous function
完成此任务的最佳和最差方法是 Exception。 不推荐这种方式,但这种方式可以解决您的问题:
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 扩展有一个很好的答案: How to make a PHP extension
array_reduce_2();
但是中断实现存在问题。
需要检测哪个条件失效。
在实现下面,array_reduce_2
检查回调是否返回。
如果是这样 - 中断执行。
这种方式允许通过检查值的返回类型来检查执行是否中断。
根据@wordragon 通知,也实现了将关联数组作为参数传递的功能。
function array_reduce_2( $array, $callback, $initial = null ) {
$len = count( $array );
$index = 0;
if ( $len == 0 && count( func_get_args() ) == 1 ) {
return null;
}
$values = array_values( $array );
$result = $initial ?? $values[ $index ++ ];
foreach ( $values as $value ) {
$result = $callback( $result, $value );
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
实现才能正常工作。
答案 3 :(得分:1)
我建议改用 foreach
循环。不使用 array_reduce
的原因是:
mixed
,因此如果您滥用结果,检查不会显示错误,或者如果您正确使用它,它们可能会显示误报。$result
并在循环中添加它(或您所做的任何事情)比理解某些东西被 return
处理然后在下一个中作为 $carry
累加器传递更容易阅读打电话。use
关键字传递参数,这确实比非回调更难阅读。答案 4 :(得分:0)
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;
附言我刚刚在本地编写并对其进行了全面测试。