Мобильная
версия

Массивы и итераторы (часть 1)

Дата: Категория: PHP

С массивами в программировании невозможно не столкнуться. Они - огромная и очень полезная часть любого кода.

Сегодня речь пойдет об итераторах.

"Итератор (от англ. iterator) — объект, абстрагирующий за единым интерфейсом доступ к элементам коллекции. Итератор иногда также называют курсором, особенно если речь идет о базе данных. В Обероне он называется также бегуно́к и представлен как тип данных. В простейшем случае итератором в низкоуровневых языках является указатель."
(c) Wikipedia

То есть итератор представляет из себя список чего либо, и с помощью итератора мы можем проходить по этому списку и выбирать нужные значения.
Он работает немного не так как простые массивы - он просто перемещает указатель и забирает то значение на которое указывает текущий указатель.

Но зачем?

Сразу перейдем к примеру.

$array = range(1,1000000);

Подробнее о функции range можно прочитать здесь. Эта функция создает массив элементов.

Если запустить этот код, то мы увидим ошибку - Allowed memory size of 134217728 bytes exhausted (tried to allocate 32 bytes)
Нам не хватает памяти. (на разных компьютерах и с разными настройками конечное значение range может варьироваться, но в любом случае увеличивая его мы так или иначе получим эту ошибку)

А теперь посмотрим на другой пример кода - он конечно немного подлиннее (о нем я расскажу позже), но делает он именно тоже самое - создает массив из 1000000 элементов.

class RangeIterator implements Iterator
{
    private $offset, $index, $length, $step;
    public function __construct($low, $high, $step = NULL)
    {
        $low  = $this->inputArgumentNumeric('Low', $low);
        $high = $this->inputArgumentNumeric('High', $high);
        if ($step === NULL) {
            $step = 1;
        }
        if (!is_integer($step) and !is_float($step)) {
            throw new InvalidArgumentException(sprintf('Step must be integer or float, %s given', gettype($step)));
        }
        $this->offset = $low;
        $step         = abs($step);
        $this->length = floor(abs(($high - $low) / $step)) + 1;
        $this->step   = $low > $high ? -$step : $step;
    }
    public function rewind()
    {
        $this->index = 0;
    }
    public function valid()
    {
        return $this->index < $this->length;
    }
    public function current()
    {
        return $this->offset + $this->index * $this->step;
    }
    public function key()
    {
        return $this->index;
    }
    public function next()
    {
        $this->index < $this->length
        && $this->index++;
    }
    private function inputArgumentNumeric($name, $value)
    {
        if (!is_numeric($value)) {
            throw new InvalidArgumentException(sprintf("%s must be a number or a numeric string, %s given", $name, gettype($value)));
        }
        if (is_string($value)) {
            $int = (int)$value;
            return "$int" === $value ? (int)$value : (float)$value;
        }
        if (is_int($value)) {
            return $value;
        }
        return (float)$value;
    }
}
$obj = new RangeIterator(1,1000000);
foreach($obj as $item){}
Если запустить этот код (можете даже поэкспериментировать с прибавлением к нашему числу нулей), то никакой ошибки не будет и код отработает на ура.

Почему же так происходит?

Все просто - когда мы создаем простой массив (функция range) мы полностью выгружаем его в память и работаем с ним. Для того чтобы получить даже один из его элементов нам потребуется выделить память для всего массива, и если массив очень большой то памяти может и не хватить.

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

К итераторам можно обращаться также как и к массивам и производить практически все действия.

Область применение

Область применения крайне разнообразна - от инкапсуляции взаимодействия с элементами массива (например если каждый элемент нужно умножить на 2 то всеголишь стоит дописать соответствующее действие в функцию current), до простой экономии памяти.
Особенно это очень удобно при парсинге, например, xml фидов, или огромных csv файлов. При использовании итераторов вам не придется выделять память под создание массива из объектов - только под файл.

В первой части я описал немного теории, а в следующей заметке мы перейдем непосредственно к реализации и разбору кода.

Теги: #php, #ООП, # Оптимизация

Ваша оценка:

Рейтинг: 9.8 (Оценок: 2)

Комментарий:

Copyright © DOC_tr 2015-2017 г. Все права защищены
Яндекс.Метрика
Перейти к мобильной версии