简体   繁体   English

PHP 惰性数组映射

[英]PHP lazy array mapping

Is there a way of doing array_map but as an iterator?有没有办法做 array_map 但作为迭代器?

For example:例如:

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

The reason to do this is that $function is hard to calculate and $array has too many elements, only need to map until I find a specific value.这样做的原因是 $function 很难计算并且 $array 的元素太多,只需要 map 直到我找到一个特定的值。 array_map will calculate all values before I can search for the one I want. array_map 将计算所有值,然后我才能搜索我想要的值。

I could implement the iterator myself, but I want to know if there is a native way of doing this.我可以自己实现迭代器,但我想知道是否有这样做的本机方式。 I couldn't find anything searching PHP documentation.我在搜索 PHP 文档时找不到任何内容。

In short: No.简而言之:没有。

There is no lazy iterator mapping built into PHP. PHP 中没有内置惰性迭代器映射。 There is a non-lazy function iterator_apply() , but nothing like what you are after.有一个非懒惰的 function iterator_apply() ,但与您所追求的完全不同。

You could write one yourself, as you said.正如你所说,你可以自己写一个。 I suggest you extend IteratorIterator and simply override the current() method.我建议您扩展IteratorIterator并简单地覆盖current()方法。

If there were such a thing it would either be documented here or here .如果有这样的事情,它会记录在这里这里

This is a lazy collection map function that gives you back an Iterator :这是一个惰性集合 map function 给你一个Iterator

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

I'm thinking of a simple Map class implementation which uses an array of keys and an array of values.我正在考虑一个简单的 Map class 实现,它使用一个键数组和一个值数组。 The overall implementation could be used like Java's Iterator class whereas you'd iterate through it like:整体实现可以像 Java 的 Iterator class 一样使用,而您可以像这样迭代它:

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

Don't bother with an iterator, is the answer:不要打扰迭代器,答案是:

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

I wrote this class to use a callback for that purpose.我写了这个 class 来为此目的使用回调。 Usage:用法:

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

Definition (feel free to modify for your need):定义(根据需要随意修改):

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's iterators are quite cumbersome to use, especially if deep nesting is required. PHP 的迭代器使用起来非常麻烦,尤其是在需要深度嵌套的情况下。 LINQ, which implements SQL-like queries for arrays and objects, is better suited for this, because it allows easy method chaining and is lazy through and through. LINQ 实现了对 arrays 和对象的类似 SQL 的查询,更适合这种情况,因为它允许简单的方法链接并且是懒惰的。 One of the libraries implementing it is YaLinqo *.实现它的库之一是YaLinqo *. With it, you can perform mapping and filtering like this:有了它,您可以像这样执行映射和过滤:

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

There're many more functions, over 70 overall.还有更多功能,总共超过 70 个。

* developed by me * 由我开发

Have a look at Non-standard PHP library .查看非标准 PHP 库 It has a lazy map function:它有一个懒惰的 map function:

use function \nspl\a\lazy\map;

$heavyComputation = function($value) { /* ... */ };
$iterator = map($heavyComputation, $list);
foreach ($array as $key => $value) {
   if ($value === $required) {
      break;
   } else {
      $array[$key] = call_back_function($value);
   }
}

process and iterate until required value is found.处理和迭代,直到找到所需的值。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM