【问题标题】:PHP lazy array mappingPHP 惰性数组映射
【发布时间】:2011-09-27 19:27:46
【问题描述】:

有没有办法做 array_map 但作为一个迭代器?

例如:

foreach (new MapIterator($array, $function) as $value)
{
   if ($value == $required)
      break;
}

这样做的原因是$function很难计算并且$array的元素太多,只需要映射直到找到一个特定的值。 array_map 将计算所有值,然后我才能搜索到我想要的值。

我可以自己实现迭代器,但我想知道是否有本地方法可以做到这一点。我在搜索 PHP 文档时找不到任何内容。

【问题讨论】:

    标签: php spl


    【解决方案1】:

    简而言之:没有。

    PHP 中没有内置惰性迭代器映射。有一个非惰性函数 iterator_apply(),但与您所追求的完全不同。

    如你所说,你可以自己写一个。我建议你扩展 IteratorIterator 并简单地覆盖 current() 方法。

    如果有这样的事情,它将被记录在herehere

    【讨论】:

    • 使用IteratorIterator确实是个好主意,而不是编写一个完整的装饰器来实现Iterator接口。
    【解决方案2】:

    这是一个惰性集合映射函数,它会返回一个Iterator

    /**
     * @param array|Iterator $collection
     * @param callable $function
     * @return Iterator
     */
    function collection_map( $collection, callable $function ) {
        foreach( $collection as $element ) {
            yield $function( $element );
        }
    }
    

    【讨论】:

    • 将其设为免费函数而不是 Collection 类成员函数的好主意
    【解决方案3】:

    我正在考虑一个简单的 Map 类实现,它使用一个键数组和一个值数组。整体实现可以像 Java 的 Iterator 类一样使用,而您可以像这样迭代它:

    while ($map->hasNext()) {
      $value = $map->next();
      ...
    }
    

    【讨论】:

      【解决方案4】:
      foreach ($array as $key => $value) {
         if ($value === $required) {
            break;
         } else {
            $array[$key] = call_back_function($value);
         }
      }
      

      处理和迭代直到找到所需的值。

      【讨论】:

        【解决方案5】:

        不要打扰迭代器,答案是:

        foreach ($array as $origValue)
        {
           $value = $function($origValue);
           if ($value == $required)
              break;
        }
        

        【讨论】:

        • Aaa 这被否决了,因为?没有其他人提供了如何执行 OP 要求的示例,没有副作用。
        • 我没有投反对票,但这不是一个懒惰的解决方案,只是一个短路的解决方案。它仅涵盖示例,而不是 OP 可能打算的所有情况。 (虽然接受的答案没有提供任何代码,但它确实指向了正确的方向。)
        【解决方案6】:

        我写这个类是为了使用回调来达到这个目的。用法:

        $array = new ArrayIterator(array(1,2,3,4,5));
        $doubles = new ModifyIterator($array, function($x) { return $x * 2; });
        

        定义(根据需要随意修改):

        class ModifyIterator implements Iterator {
            /**
             * @var Iterator
             */
            protected $iterator;
        
            /**
             * @var callable Modifies the current item in iterator
             */
            protected $callable;
        
            /**
             * @param $iterator Iterator|array
             * @param $callable callable This can have two parameters
             * @throws Exception
             */
            public function __construct($iterator, $callable) {
                if (is_array($iterator)) {
                    $this->iterator = new ArrayIterator($iterator);
                }
                elseif (!($iterator instanceof Iterator))
                {
                    throw new Exception("iterator must be instance of Iterator");
                }
                else
                {
                    $this->iterator = $iterator;
                }
        
                if (!is_callable($callable)) {
                    throw new Exception("callable must be a closure");
                }
        
                if ($callable instanceof Closure) {
                    // make sure there's one argument
                    $reflection = new ReflectionObject($callable);
                    if ($reflection->hasMethod('__invoke')) {
                        $method = $reflection->getMethod('__invoke');
                        if ($method->getNumberOfParameters() !== 1) {
                            throw new Exception("callable must have only one parameter");
                        }
                    }
                }
        
                $this->callable = $callable;
            }
        
            /**
             * Alters the current item with $this->callable and returns a new item.
             * Be careful with your types as we can't do static type checking here!
             * @return mixed
             */
            public function current()
            {
                $callable = $this->callable;
                return $callable($this->iterator->current());
            }
        
            public function next()
            {
                $this->iterator->next();
            }
        
            public function key()
            {
                return $this->iterator->key();
            }
        
            public function valid()
            {
                return $this->iterator->valid();
            }
        
            public function rewind()
            {
                $this->iterator->rewind();
            }
        }
        

        【讨论】:

          【解决方案7】:

          PHP 的迭代器使用起来非常麻烦,尤其是在需要深度嵌套的情况下。 LINQ 实现了对数组和对象的类似 SQL 的查询,更适合这种情况,因为它允许简单的方法链接并且是懒惰的。实现它的库之一是YaLinqo*。有了它,您可以像这样执行映射和过滤:

          // $array can be an array or \Traversible. If it's an iterator, it is traversed lazily.
          $is_value_in_array = from($array)->contains(2);
          
          // where is like array_filter, but lazy. It'll be called only until the value is found.
          $is_value_in_filtered_array = from($array)->where($slow_filter_function)->contains(2);
          
          // select is like array_map, but lazy.
          $is_value_in_mapped_array = from($array)->select($slow_map_function)->contains(2);
          
          // first function returns the first value which satisfies a condition.
          $first_matching_value = from($array)->first($slow_filter_function);
          // equivalent code
          $first_matching_value = from($array)->where($slow_filter_function)->first();
          

          还有更多功能,总共超过 70 个。

          *我开发的

          【讨论】:

            【解决方案8】:

            看看Non-standard PHP library。它有一个lazy map 函数:

            use function \nspl\a\lazy\map;
            
            $heavyComputation = function($value) { /* ... */ };
            $iterator = map($heavyComputation, $list);
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2011-07-03
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2020-03-17
              相关资源
              最近更新 更多