PHP懒惰数组映射

15

有没有一种类似于array_map但是返回迭代器的方法?

例如:

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

这样做的原因是$function难以计算,而$array元素过多,只需映射到找到特定值为止。使用array_map将在我搜索所需值之前计算所有值。

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

8个回答

6
简而言之:没有。
PHP中没有内置的惰性迭代器映射。有一个非惰性函数iterator_apply(),但没有像你所需的那样的东西。
你可以自己编写一个,就像你说的一样。我建议你扩展IteratorIterator并简单地覆盖current()方法。
如果有这样的东西,它要么在这里这里或者这里被记录。

使用IteratorIterator确实是个好主意,而不是编写一个完整的装饰器实现Iterator接口。 - Harmen

6

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

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

将这个函数作为免费函数而不是Collection类成员函数是一个好主意。 - Harmen

1
我正在考虑一个简单的Map类实现,它使用键数组和值数组。整个实现可以像Java的Iterator类一样使用,你可以通过迭代器遍历它:
while ($map->hasNext()) {
  $value = $map->next();
  ...
}

0
foreach ($array as $key => $value) {
   if ($value === $required) {
      break;
   } else {
      $array[$key] = call_back_function($value);
   }
}

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


0

PHP的迭代器使用起来相当麻烦,特别是在需要深度嵌套时。实现了针对数组和对象的类SQL查询的LINQ更适合这种情况,因为它允许轻松的方法链接,并且始终是惰性的。其中一个实现它的库是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个。

* 由我开发


0

不必使用迭代器,这就是答案:

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

这个被踩的原因是什么?没有其他人提供一个例子来回答OP的问题,而且还要避免副作用。 - Izkata
我没有给它点踩,但那不是一种懒惰的解决方案,只是一种短路解决方案。它只涵盖了示例,而不是OP可能打算覆盖的整个情况范围。(尽管被接受的答案没有提供任何代码,但它确实指出了正确的方向。) - Brilliand

0
我编写了这个类来使用回调函数实现这个目的。用法:
$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();
    }
}

0

看看非标准PHP库。它有一个惰性映射函数:

use function \nspl\a\lazy\map;

$heavyComputation = function($value) { /* ... */ };
$iterator = map($heavyComputation, $list);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接