PHP惰性数组映射


PHP lazy array mapping

除了迭代器之外,还有什么方法可以实现array_map吗?

例如:

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

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

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

简而言之:没有

PHP中没有内置懒惰迭代器映射。有一个非懒惰函数迭代器应用程序((,但与您所追求的完全不同。

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

如果有这样的事情,要么在这里记录,要么在这儿记录。

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

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

我想到了一个简单的Map类实现,它使用一个键数组和一个值数组。整体实现可以像Java的Iterator类一样使用,而您可以像一样迭代它

while ($map->hasNext()) {
  $value = $map->next();
  ...
}
foreach ($array as $key => $value) {
   if ($value === $required) {
      break;
   } else {
      $array[$key] = call_back_function($value);
   }
}

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

不要麻烦迭代器,答案是:

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

我编写这个类是为了使用回调。用法:

$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();
    }
}

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个。

*由我开发

了解一下非标准PHP库。它有一个懒惰的地图功能:

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