【问题标题】:How to exit from array iteration functions (array_reduce) in PHP如何在 PHP 中退出数组迭代函数(array_reduce)
【发布时间】:2015-07-14 00:31:41
【问题描述】:

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

【问题讨论】:

  • 代替break;添加return;
  • Return 只会退出回调函数。在这种情况下,返回就像一个“继续”;
  • 你不能及早脱离 reduce 函数。我想你可以使用异常,但这似乎是一个糟糕的主意。也许你的逻辑更类似于find
  • 我同意。在这种情况下,异常看起来几乎像“goto”。什么是查找?
  • 你可以在输入array_reduce之前过滤$input数组,这就是我处理这种情况的方式。

标签: php arrays loops array-reduce


【解决方案1】:

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

【讨论】:

    【解决方案2】:

    首先,让我说 array_reduce 可能是我最喜欢的函数之一 - 我以使用 40 行清晰编写的代码并将它们替换为 4 行更难理解的 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
    

    【讨论】:

      【解决方案3】:

      根据类似的答案。

      Break array_walk from anonymous function

      完成这个的最好和最坏的方法是例外。 不推荐这种方式,但是这种方式可以解决你的问题:

      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 实现只有在您不需要将正常值作为回调返回时才能正常工作。

      【讨论】:

      • 这也假设数组是数字键控的,array_reduce 不需要。您可以通过将数组转换为带有 array_values 的数字索引数组来轻松解决此问题。
      【解决方案4】:

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

      合理的原因:

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

      自以为是的原因:

      1. 眼睛比较难看。拥有一个 $result 并在循环中添加(或任何你做的)比掌握某个东西是 returned 然后在下一次调用中作为 $carry 累加器传递更容易阅读。
      2. 让我懒得正确提取函数。如果我将一个操作提取到回调中,我可能会发现代码足够短,无法将整个数组操作提取到一个真正应该首先完成的函数。
      3. 如果您使用条件中断,则很有可能有一天您可能需要该回调函数的其他参数。修复回调签名后,您必须使用 use 关键字传递参数,这比非回调更难阅读。

      【讨论】:

        【解决方案5】:

        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;
        

        附:我只是在本地编写并对其进行了全面测试。

        【讨论】:

          猜你喜欢
          • 2016-09-01
          • 1970-01-01
          • 1970-01-01
          • 2019-02-08
          • 2016-02-25
          • 1970-01-01
          • 2015-08-14
          • 1970-01-01
          • 2021-12-28
          相关资源
          最近更新 更多