массивы — сохранить внутренний указатель после foreach () на объекте Iterator

Ниже приведены коды из PHP документа о Итератор с добавлением нескольких строк, чтобы показать позицию.

Как вы можете видеть, объект имеет 3 элемента, и позиция находится на втором элементе ($ this-> position = 1) перед foreach (),

После foreach () позиция изменяется на недопустимое значение ($ this-> position = 3).

class myIterator implements Iterator {
private $position = 0;
private $array = array(
"firstelement",
"secondelement",
"lastelement",
);

public function __construct() {
$this->position = 0;
}

public function rewind() {
var_dump(__METHOD__);
$this->position = 0;
}

public function current() {
var_dump(__METHOD__);
return $this->array[$this->position];
}

public function key() {
var_dump(__METHOD__);
return $this->position;
}

public function next() {
var_dump(__METHOD__);
++$this->position;
}

public function valid() {
var_dump(__METHOD__);
return isset($this->array[$this->position]);
}

public function showPosition() {
return $this->position;
}

}

$it = new myIterator;

$it->next();
var_dump($it->showPosition());   //shows 1

foreach($it as $key => $value) {
var_dump($key, $value);
echo "\n"; }

var_dump($it->showPosition()); //shows 3 which is an invalid value.

в foreach () документ это показывает:

Примечание: в PHP 5 …. В PHP 7 foreach не использует внутренний указатель массива.

Я использую PHP7, и, очевидно, приведенные выше примеры кода показывают, что внутренняя точка изменилась после foreach ().

Мой вопрос — возможно ли сохранить исходную позицию после foreach ()?

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

-2

Решение

Указатель в вашей реализации Itterator неверен после цикла, потому что вы не устанавливаете / сбрасываете значение при последнем вызове valid() из цикла foreach он сохраняет последнее введенное значение, которое находится за пределами массива (в этом случае 3 ), здесь проще всего установить указатель null поэтому он отражает поведение PHP (что вы получите, когда используете встроенные функции массива внутри итератора, подробнее об этом позже)

Стоит отметить, что в PHP7 произошли изменения

Примечание: в PHP 5, когда foreach впервые начинает выполняться, внутренний
указатель массива автоматически сбрасывается на первый элемент
массив. Это означает, что вам не нужно вызывать reset () перед
цикл foreach. Поскольку foreach полагается на внутренний указатель массива в PHP
5, изменение его в цикле может привести к неожиданному поведению. В PHP
7, foreach не использует внутренний указатель массива.

Лично я не вижу в этом большой проблемы, если вы не передаете внутренний указатель. Что, по моему опыту, не так много.

Теперь, чтобы исправить вашу реализацию Iterator:

Опция 1:

Позвольте PHP обрабатывать отслеживание указателя с помощью встроенных функций массива:

class myIterator implements Iterator {

private $array = array(
"firstelement",
"secondelement",
"lastelement",
);

public function __construct() {
reset( $this->array );
}

public function rewind() {
reset( $this->array );
}

public function current() {
return current( $this->array );
}

public function key() {
return key( $this->array );
}

public function next() {
next( $this->array );
}

public function valid() {
return isset( $this->array[$this->key()] );
}

//alias of key()
public function showPosition() {
return $this->key();
}
}echo str_pad('= NATIVE TRACKED POINTER =', 60, '=') ."\n";

$it = new myIterator;

$it->next(); //move to index 1

var_dump($it->key());   //<-- should print 1

foreach($it as $key => $value) {}

var_dump($it->key()); //<--shows NULL,

echo str_pad('= NATIVE ARRAY POINTER =', 60, '=') ."\n";

$array = array(
"firstelement",
"secondelement",
"lastelement",
);

next($array); //move to index 1

var_dump(key($array)); //<-- should print 1
foreach( $array as $key => $value ){}
var_dump(key($array));  //<--  NULL (PHP < 7),  1 PHP 7+

Это выводит (в PHP7.0.1)

= NATIVE TRACKED POINTER ===================================
int(1)
NULL
= NATIVE ARRAY POINTER =====================================
int(1)
NULL

Это выводит (в PHP5.6.29)

= MANUAL TRACKED POINTER ===================================
int(1)
NULL
= NATIVE ARRAY POINTER =====================================
int(1)
NULL

Option2:

Обязательно сбросьте $position в $myIterator->valid()' when the pointer is out of bounds. (This should be done after the call toдействительный ()you could check on next, by setting the pointer toNULL` мы можем, по крайней мере, подражать поведению, которое вы бы получили, используя отслеживание родного указателя)

class myIterator implements Iterator {
private $position = 0;
private $array = array(
"firstelement",
"secondelement",
"lastelement",
);

public function __construct() {
$this->position = 0;
}

public function rewind() {
$this->position = 0;
}

public function current() {
return $this->array[$this->position];
}

public function key() {
return $this->position;
}

public function next() {
++$this->position;
}

public function valid() {
$valid = isset($this->array[$this->position]);

if(!$valid){
$this->position = null;
}

return $valid;
}

public function showPosition() {
return $this->position;
}

}

echo str_pad('= MANUAL TRACKED POINTER =', 60, '=') ."\n";

$it = new myIterator;

$it->next(); //move to index 1

var_dump($it->key());   //<-- should print 1

foreach($it as $key => $value) {}

var_dump($it->key()); //<--shows NULL (PHP < 7),

echo str_pad('= NATIVE ARRAY POINTER =', 60, '=') ."\n";

$array = array(
"firstelement",
"secondelement",
"lastelement",
);

next($array); //move to index 1

var_dump(key($array)); //<-- should print 1
foreach( $array as $key => $value ){}
var_dump(key($array));  //<--  NULL (PHP < 7),  1 PHP 7+

Для Option2 я настроил его так, чтобы он отражал нативное поведение PHP < 7, потому что вы вручную отслеживаете указатель массива, нет простого способа сделать это только с Iterator интерфейс.

http://sandbox.onlinephpfunctions.com/code/2d38af15e5b0269dcdd341d0a77b601ce2713cce

Это выводит (в PHP7.0.1)

= MANUAL TRACKED POINTER ===================================
int(1)
int(0)
= NATIVE ARRAY POINTER =====================================
int(1)
int(1)

Это выводит (в PHP5.6.29)

= MANUAL TRACKED POINTER ===================================
int(1)
NULL
= NATIVE ARRAY POINTER =====================================
int(1)
NULL

ОБНОВИТЬ

Теперь, если вы хотите полностью эмулировать поведение PHP7, потребуется гораздо больше работы. (Я уверен, что может быть другой способ сделать это, но это способ, которым я придумал)

class myIterator implements IteratorAggregate{
protected $innerIterator = [];

public function __construct(array $array = []){
$this->innerIterator = new myInnerIterator($array,$this);
}

public function getIterator(){
$this->innerIterator->cachePointer();
return $this->innerIterator;
}

public function seek($index){
$this->innerIterator->seek($index);
}

public function rewind() {
$this->innerIterator->rewind();
}

public function current() {
return $this->innerIterator->current();
}

public function key() {
return $this->innerIterator->key();
}

public function next() {
$this->innerIterator->next();
}

public function valid() {
return $this->innerIterator->valid();
}
}

class myInnerIterator implements SeekableIterator{

protected $array;

protected $pointer_cache = null;

public function __construct( array $array = [], $wrapper){
if( !is_a($wrapper, 'myIterator') ) throw new Exception('myInnerIterator can only be constructed by myIterator');
$this->array = new ArrayIterator($array);
}

public function cachePointer(){
$this->pointer_cache = $this->key();
}

public function seek($index){
$this->array->seek($index);
}

public function rewind() {
$this->array->rewind();
}

public function current() {
return $this->array->current();
}

public function key() {
return $this->array->key();
}

public function next() {
$this->array->next();
}

public function valid() {
$valid = $this->array->valid();
if(!$valid && $this->pointer_cache ){
if( defined('PHP_VERSION_ID') && PHP_VERSION_ID >= 70000 )
$this->seek( $this->pointer_cache );
$this->pointer_cache = null;
}
return $valid;
}

}

echo str_pad('= CACHED  POINTER =', 60, '=') ."\n";

$array = array(
"firstelement",
"secondelement",
"lastelement",
);

$it = new myIterator($array);

$it->next(); //move to index 1

var_dump($it->key());   //<-- should print 1

foreach($it as $key => $value) {}

var_dump($it->key()); //<--shows NULL,

echo str_pad('= NATIVE ARRAY POINTER =', 60, '=') ."\n";

next($array); //move to index 1

var_dump(key($array)); //<-- should print 1
foreach( $array as $key => $value ){}
var_dump(key($array));  //<--  NULL (PHP < 7),  1 PHP 7+

И это выводит

Это выводит (в PHP7.0.1)

= CACHED POINTER ===================================
int(1)
int(1)
= NATIVE ARRAY POINTER =====================================
int(1)
int(1)

Это выводит (в PHP5.6.29)

= CACHED POINTER ===================================
int(1)
NULL
= NATIVE ARRAY POINTER =====================================
int(1)
NULL

Вот песочница, где вы можете проверить это

http://sandbox.onlinephpfunctions.com/code/66dc26003347ec40184bb0283e78a3d67e86c51a

Таким образом, вы никогда не должны касаться myInnerIterator класс, потому что он завернут в myIteratorна самом деле, если вы попытаетесь создать его, не передавая ему экземпляр myIterator, он выдаст исключение. Хитрость тебе нужна IteratorAggregate::getIterator Который называется, когда foreach начинается, а затем SeekableIterator::seekКроме того, кэширование позиции должно быть сделано в InnerIterator. Что еще называет getIterator автоматически, и что все это будет влиять, я действительно не знаю с макушкой.

В любом случае, надеюсь, что это помогает или дает вам некоторые идеи.

0

Другие решения

Других решений пока нет …

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector