PHP ленивое отображение массивов - PullRequest
15 голосов
/ 04 июля 2011

Есть ли способ сделать array_map, но в качестве итератора?

Например:

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

Причина этого в том, что $ function сложно вычислить, а $ array имеетслишком много элементов, нужно только сопоставить, пока я не найду конкретное значение.array_map вычислит все значения, прежде чем я смогу найти нужное.

Я мог бы сам реализовать итератор, но я хочу знать, есть ли собственный способ сделать это.Я не смог найти ничего, что искало бы документацию PHP.

Ответы [ 8 ]

6 голосов
/ 04 июля 2011

Короче говоря: №

В PHP нет встроенного отображения ленивых итераторов. Есть не ленивая функция iterator_apply () , но она не похожа на то, что вам нужно.

Ты мог бы написать сам, как ты сказал. Я предлагаю вам расширить IteratorIterator и просто переопределить метод current () .

Если бы была такая вещь, это было бы документировано здесь или здесь .

5 голосов
/ 19 августа 2015

Это ленивая функция карты коллекции, которая возвращает вам Iterator:

/**
 * @param array|Iterator $collection
 * @param callable $function
 * @return Iterator
 */
function collection_map( $collection, callable $function ) {
    foreach( $collection as $element ) {
        yield $function( $element );
    }
}
1 голос
/ 04 июля 2011

Я имею в виду простую реализацию класса Map, которая использует массив ключей и массив значений. Полная реализация может быть использована как класс Iterator для Java, тогда как вы будете выполнять итерацию по нему следующим образом:

while ($map->hasNext()) {
  $value = $map->next();
  ...
}
0 голосов
/ 04 февраля 2017

Взгляните на Нестандартную библиотеку PHP . Имеет функцию Ленивая карта :

use function \nspl\a\lazy\map;

$heavyComputation = function($value) { /* ... */ };
$iterator = map($heavyComputation, $list);
0 голосов
/ 04 июня 2015

Итераторы 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 в целом.

* разработано мной

0 голосов
/ 10 июля 2014

Я написал этот класс для использования обратного вызова для этой цели.Использование:

$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 голосов
/ 17 апреля 2012

Не связывайтесь с итератором, вот ответ:

foreach ($array as $origValue)
{
   $value = $function($origValue);
   if ($value == $required)
      break;
}
0 голосов
/ 04 июля 2011
foreach ($array as $key => $value) {
   if ($value === $required) {
      break;
   } else {
      $array[$key] = call_back_function($value);
   }
}

обрабатывать и повторять до тех пор, пока не будет найдено требуемое значение.

...