Почему я должен перематывать IteratorIterator
Вопрос
$arrayIter = new ArrayIterator( array(1, 2) );
$iterIter = new IteratorIterator($arrayIter);
var_dump($iterIter->valid()); //false
var_dump($arrayIter->valid()); //true
Если я сначала позвоню $iterIter->перемотка(), затем $iterIter->действительный() правда.Мне любопытно, почему требуется вызов rewind().Я полагаю, что для этого есть веская причина, но я ожидал, что он просто начнет итерацию в любом состоянии, в котором находится его внутренний итератор, и оставит возможность перемотки назад перед началом итерации.
вызов next() также, кажется, переводит его в «действительное» состояние (хотя он переходит на следующую позицию, предполагая, что ранее он был на первой позиции).
$arrayIter = new ArrayIterator(array(1,2));
$iterIter = new IteratorIterator($arrayIter);
$iterIter->next();
var_dump($iterIter->valid());
Опять же, мне любопытно, почему мне нужно вызывать rewind(), несмотря на то, что внутренний итератор находится в допустимом состоянии.
Решение
При использовании нового итератора позиция не инициализируется просто из соображений производительности. Вы можете размещать итераторы поверх других итераторов. Если все они будут перематываться во время построения, это повлияет на производительность, кроме того, некоторые итераторы могут изменить свое первое значение после конструктор был выполнен, что неизвестно дальнейшим итераторам.
Итераторы обычно выполняются функцией foreach(), которая сначала выполняет rewind()...
Другие советы
Продлевая IteratorIterator
class, чтобы сэкономить на реализации всего интерфейса итератора и/или создать декоратор итератора, с которым я тоже столкнулся.
Этот декоратор уже является решением проблемы, ему нужно только реализовать недостающую функциональность, чтобы устранить несогласованность.Нет необходимости в автоматической перемотке:
class IteratorDecorator extends IteratorIterator
{
public function valid()
{
return $this->getInnerIterator()->valid();
}
}
Пример:Если у вас есть Iterator
объект, который действителен по умолчанию, например. ArrayIterator
:
$it = new ArrayIterator(array(1));
var_dump($it->valid()); # bool(true)
$itit = new IteratorIterator($it);
var_dump($itit->valid()); # bool(false)
Это свидетельствует о противоречивости IteratorIterator
реализация ну, IteratorIterator
объект не отражает должным образом внутреннее ArrayIterator
состояние.Используя IteratorDecorator
может вылечить это:
$decor = new IteratorDecorator($it);
var_dump($decor->valid()); # bool(true)
И если вы дочитали до этого момента, вот еще один особый случай, который вы, возможно, захотите рассмотреть:Если вам не нужно иметь rewind
с внутренним итератором, вы можете просто использовать NoRewindIterator
который также возвращает правильность действительности:
$noretit = new NoRewindIterator($it);
var_dump($noretit->valid()); # bool(true)
Если принять во внимание аргументы Йоханнеса об отсутствии автоматической перемотки, это имеет смысл, поскольку NoRewindIterator
ожидает, что итератор не должен перематываться, и правильно показывает допустимость внутреннего итератора.
Но поскольку IteratorDecorator
показывает, я также не делаю никакой автоматической перемотки, чтобы устранить несогласованность.
Как сказал @johannes, позиция не инициализируется в IteratorIterator
и, следовательно, он недействителен до тех пор, пока на нем не будут запущены какие-либо другие его методы или он не будет использоваться с foreach().
Стараться сделать
var_dump( $iterIter->current() ); // NULL
var_dump( $iterIter->getInnerIterator()->current() ); // 1
А также
$iterIter->rewind();
var_dump( $iterIter->current() ); // 1
var_dump( $iterIter->getInnerIterator()->->current() ); // 1
А также обратите внимание, что в унифицированном IteratorIterator:
$iterIter->next(); // 2
var_dump( $iterIter->current()) ; // 2 (from NULL to 2)
var_dump( $iterIter->getInnerIterator()->current() ); // 2
Обратите внимание, что $arrayIter
из вашего фрагмента кода идентичен $iterIter->getInnerIterator()
.
Надеюсь, это пролило некоторый свет.