11

我有一个 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 吗?

4

5 回答 5

4

array_reduce用于编写函数式代码,该代码始终遍历整个数组。您可以重写以使用常规 foreach 循环来实现短路逻辑,或者您可以简单地返回$carrier未修改的电流。这仍然会遍历您的整个数组,但不会改变结果(正如您所说,这更像是continue

于 2018-08-26T15:20:49.223 回答
2

首先,让我说 array_reduce 可能是我最喜欢的函数之一 - 我以 40 行清晰编写的代码并将它们替换为四个更难遵循的 10 行 array_reduce 调用而闻名(嗯,在一个很小的圈子里)做同样的事!

可悲的是,PHP 数组函数似乎注定要完成它们的任务。这与无法生成递归未命名函数相结合,使得这种常见情况难以处理。不想在我的代码中添加很多难看的 for 循环,我倾向于将它们埋在另一个函数中(参见下面的 reduce),就像之前的海报一样。

值得指出的是,这并不像使用数组函数那样有效,而且在大多数情况下,最好让数组 reduce 函数使用“完成”标志来快速遍历不需要的值。无论如何,这与 array_reduce 类似(评估函数使用 null 返回来指示其完成)。目标是将数组中的数字相加,直到得到 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
于 2021-03-15T03:57:13.917 回答
1

根据类似的答案。

从匿名函数中中断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 扩展有一个很好的答案: How to make a PHP extension

array_reduce_2();

但是中断实施存在问题。需要检测哪些条件无法正常工作。下面的实现,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仅当您不需要将正常值作为回调返回时,此实现才能正常工作。

于 2021-03-14T09:50:46.457 回答
1

我建议改用foreach循环。不使用的原因array_reduce是:

声音原因:

  1. 它不是静态类型检查的。因此,如果输入或回调参数中有任何类型错误,代码检查不会显示类型错误。
  2. 它返回mixed,因此如果您滥用结果,检查不会显示错误,或者如果您正确使用它,它们可能会显示误报。
  3. 你不能打破。

自以为是的原因:

  1. 对眼睛来说更难。在循环中添加 a$result并添加到它(或任何你做的事情)比理解某些东西是returned 然后$carry在下一次调用中作为累加器传递要容易得多。
  2. 这让我懒得正确提取函数。如果我将一个操作提取到回调中,那么我可能会发现代码足够短,无法将整个数组操作提取到一个真正应该首先完成的函数。
  3. 如果您使用条件中断,则很有可能有一天您可能需要该回调函数的其他参数。修复回调签名后,您必须使用use关键字传递参数,这比非回调更难阅读。
于 2021-03-19T18:03:01.623 回答
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;

PS我刚刚在本地编写并对其进行了全面测试。

于 2021-03-19T16:18:05.610 回答