Циклы - это то, что большинство программистов училось считать само собой разумеющимся. Все знают как ими пользоваться, но никто не задумывался об этом процессе более детально. В этой статье, я исследую то, что требуется, чтобы заменить for(;;) на контролируемые циклы со счетчиком операций и покажу немного примеров, для демонстрации потенциальных выгод этого подхода.
Почти любая задача, которая требует повторения, будет требовать некоторого типа повторяющих конструкций. Существует множество решений, каждый с их собственными достоинствами и недостатками. В основном эта статья будет про различные циклы со счетчикам операций. Моя цель состоит в том, чтобы отговорить разработчиков PHP от использования for в пользу foreach там, где это возможно.

Цикл с тремя выражениями

Нигде более в PHP точка с запятой и запятая не работают как в цикле for. Введенный в язык C в начале 1970-ых, этот синтаксис остался неизменным в течение более чем трех десятилетий. Вы можете найти эхо его влияния в длинном списке современных языков программирования, включая PHP и JavaScript.

Одна причина для долговечности C-стиля цикла for, также известного как цикл с тремя выражениями, является его адаптируемостью. Именно поэтому так много программ использовали и используют его. Объединенный с break, continue и goto директивами, C-стиль цикла for не превзойден в его силе и многосторонности. На оригинальном языке C, могут быть сделаны несколько оптимизаций, так как все три условия цикла пред-объявлены. Высокоуровневые языки не часто используют полное преимущество синтаксиса, таким образом его потенциал оказывается потраченный впустую.

Использование конструкций с малым количеством ограничений несет определенные последствия. Например, синтаксис с тремя выражениями отлично приспособлен к определению бесконечных циклов - преднамеренно или случайно. Случайности, в этом случае, могут возникать по одной простой причине: им позволяют. C не помещает никаких ограничений на разработчиков, даже когда их код очевидно неблагоразумен. Бесконечные циклы, попавшие в руки начинающих программистов, были серьезным моим расстройством в прошлом. Предостережение "Быть внимательным!" кажется, не нашло своего адресата.

Цикл for дает программисту большое количество возможностей, чтобы привести к его зависанию. Для бесконечного цикла, Вам необходимо невозможное условии выхода. Поскольку условие выхода определено вручную, всегда есть столько же способов создать бесконечный цикл, как и людей. Например, цикл описанный ниже, будет зациклен бесконечно из-за одной опечатки. Такие ошибки трудно разыскать даже с хорошим дебагером:

< ?
for ($i = 0; $i < 10; ++$i) {
    if ($i = 0) {
        echo $i, "n";
    }
}
?>

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

< ?
      for ($i = 0; $i < 10; ++$i) {
          if (0 = $i) {
            echo $i, "n";
          }
      }
?>

Однако, перестановка операндов работает только как профилактическое действие в горстке ситуаций. Например, если бы ноль был переменной, то никакая ошибка не возникла. Это отлично, хотя выглядит уродливо. И уж точно это не защита от дурака. Переменные используемые для подсчета в цикле for очень хрупки. Было бы лучше написать цикл, который минимизирует воздействие на счетчик.

Итератор

Итераторы (Iterators) - объекты, которые инкапсулирую обход структур данных. Они могут работать на любом типе циклов, и они могут возвратить любой тип элемента. Итераторы могут использоваться, чтобы пройтись по связанным спискам, хэшу, массивам и даже несуществующим 'виртуальным' структурам. В этой статье, мы будем использовать итераторы для следующей цели: обход 'виртуальных' структур, типа ряда чисел. PHP начал поддерживать обход массивов начиная с 4 релиза, но реально итераторы начали поддерживать только с введением SPL (the Standard PHP Library) в PHP 5. SPL предлагает выбор из предварительно подготовленных классов итераторов. Интерфейсы делает доступным для Вас объединение стороннего кода с родными PHP конструктарами и естественно, с остальной частью SPL.
Когда объекты передаются в foreach, PHP делает некоторую работу негласно прежде, чем решить, что делать дальше. Если объект содержит встроенный интерфейс итератора, то foreach будет использовать тот интерфейс как API для выполнения цикла. Если объект содержит интерфейс IteratorAggregate, то foreach будет использовать то, что возвращено из метода getIterator(). Если объект не содержит ни одного из них умолчанию, то foreach пройдет по свойствам класса.
Использование foreach с массивами подобно использованию foreach с итераторами, но логикой обхода содержится в пределах языка, а не объекта. SPL содержит класс ArrayIterator, который резюмирует поведение должным образом. Выгода из этого тонкая, но важная. Однако, я не буду использовать ArrayIterator в этой статье кроме иллюстраций, как можно полностью осуществить итерацию массива в ориентированном интерфейсе объекта. Код ниже обманчиво бессмысленен:

< ?
$theArray = array(1, 6, 7);
$arrayItr = new ArrayIterator($theArray);
foreach ($theArray as $key => $value) {
    echo "$value,";
}

foreach ($arrayItr as $key => $value) {
    echo "$value,";
}
?>

Ряды

Большинство новых языков имеет функции диапазона (Python, PHP), операторы (Perl), или оба (Ruby). range() в PHP редко позиционируется как механизм для выполнения цикла, возможно потому что for все еще доступен. Сильные ООП языки имеют тенденцию удалять for(), по различным причинам. PHP, однако, все еще содержит этот цикл.

В PHP, range() генерирует массив от $low до $high с дополнительным $step шагом. Это - генератор массива, основанный на линейной функциии, которая по существу описывает дискретную линию числа. range() - отличный кандидат на итерируемые циклы, потому что массивы работают хорошо с foreach, и функционал уже встроен в язык. Вот два различных способа сделать одну и ту же вещь:

< ?
// standard counted loop
for ($i = 0; $i < 11; $i += 2) {
    echo $i, "n';
}
// the same loop using array traversal
foreach (range(0, 10, 2) as $i) {
    echo $i, "n";
}
?>

Обратите внимание, как в первом примере мы должны инициализировать $i, проверить $i и присвоить $i значение. Во втором случае, мы должны только определить значения начала и конца и дополнительный параметр шага. Вся инициализация и подсчет сделаны автоматически. Алгоритм уже написан, так что мы не изобретаем колесо каждый раз. Выполнение цикла этим путем может казаться счетчиком, обладающим интуицией, но итерация с массивами часто способна на тоже, что и другие методы. Вы не можете создать бесконечный цикл, и Вы не можете изменить цикл, изменяя вашу переменную счетчика. Я не предложил бы преднамеренно использовать счетчик как временную переменную, но хорошо знать, что потенциальная проблема, вызванная неумышленно, минимизирована. Лично, я также нахожу этот синтаксис проще для глаз.

XRangeIterator

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

Я написал основной код, копирующий функцию xrange() из языка Python и внутренний интерфейс итератора в PHP, результат можно увидеть в Листинге 1. Добавление xrange() в PHP решает чрезмерную проблему потребления памяти. Это также дает нам некоторые новые уловки, о которых я кратко расскажу.
Эти три утверждения ниже — три различных способа сделать одну и туже вещь:

< ?
for ($i = 0; $i <= 8; ++$i) {
    echo $i, "n";
}
foreach (range(0, 8) as $i) {
    echo $i, "n";
}
foreach (xrange(0, 8) as $i) {
    echo $i, "n"; // <-- ours
}
?>

XRangeIterator — класс, возвращенный из xrange() функции, определенной в Листинге 1. Основная цель состоит в том, чтобы генерировать те же самые значения, которые range() вывел бы, но по требованию. Класс XRangeIterator осуществляет потомка внутреннего интерфейса итераторов PHP, это значит, что использование его с foreach() происходит достаточно просто и все *Iterator классы основываются на SPL.
Даже если Вы никогда не играли с PHP итераторами прежде, Вы найдете, что они весьма просты. Вы должны только осуществить пять методов интерфейса:

* current() - возвращает текущее значение
* key() - возвращает текущий ключ
* next() - следующий ключ
* rewind() - переход в начало
* valid() - вызывается перед current() чтобы определить, должна ли итерация закончиться

Начиная с current() метод возвращает значение, это — то, куда я помещаю суть логики в классе XRangeIterator. Я хотел использовать немного более сложный алгоритм чем простое увеличение шага; Я считаю только итеративное число, и затем каждый раз вычисляю результат. Это медленнее, да, но позволяет мне централизовать логику, которая в свою очередь позволяет мне осуществлять rewind() и seek() очень просто. Чтобы перемотать, я просто выставляю значение счетчика равным нулю. Для поиска, я сначала проверяю попадает ли текущая позиция между нулем и максимальным числом итераций, и затем устанавливаю итеративный счет соответствующим образом.

Этот общий интерфейс позволяет Вам делать творческие вещи, типа объединения Вашего итератора с другими. Следующий пример просто дает идею, на тему того, что вы можете сделать. В реальности использование этого класса намного шире:

< ?
$x = xrange(700, 2048);
foreach (new LimitIterator($x, 0, 10) as $i) {
    echo $i, "n";
}
foreach (new LimitIterator($x, 10, 10) as $i) {
    echo $i, "n";
}
?>

Этот пример использует LimitIterator от SPL, который дает Вам делать срез итераторов. LimitIterator похож на объектно ориентированную версию функции array_slice(). Она выполняет проверку диапазона, таким образом Ваш код не должен даже знать о содержании в Вашем итераторе. Так как мы не распределяем никаких массивов, Вы можете использовать большие числа без проблем.

Другой полезный SPL итератор, когда он используется в связке, является AppendIterator. Используя наш новый класс XRangeIterator, мы можем сложить несколько наборов чисел вместе и выполнить итерации по всем им сразу. В правильной ситуации, это может сделать код очень изящным. Концептуально, это не очень отличается от медлнной версии array_merge(). Вы можете буквально передать все наборы чисел как единственный объект, объединить их, и обработать их соответственно. Я думаю, что это очень круто. Операция append(), сама по себе, практически не имеет ограничений:

< ?
$x = new AppendIterator;
$x->append(xrange(1, 10));
$x->append(xrange(26, 27));
$x->append(xrange(100, 105));

foreach ($x as $i) {
    echo $i, ‘,’;
}
// 1,2,3,4,5,6,7,8,9,10,26,27,100,101,102,103,104,105,
?>

Теперь, когда Вы имеете генератор диапазонов, Вы можете добавить существенное количество значений используя наследование классов. Например, здесь я использовал абстрактный класс FilterIterator, снова от SPL, для того чтобы вывести нечетные значения из моего диапазона:

< ?
class OddOnlyFilter extends FilterIterator {
    public function accept() {
       return $this->current() & 1;
    }
}
$myRange = xrange(1, 20);
foreach (new OddOnlyFilter(xrange(1, 20)) as $i) {
    echo $i, ",";
}
// 1,3,5,7,9,11,13,15,17,19,
?>

Поскольку возможно использование XRangeIterator с другим итераторами, в виде строительных блоков, эта утилита ограничена только классами, которые Вы имеете в наличии и вашим собственным воображением. Давайте возьмем пример описанный выше и добавим новый класс по имени SquareFilter. Вероятно это не надлежащее использование FilterIterator, но это удовлетворяет примеру. SquareFilter возьмет итератор, введет и возвратит квадрат значения. Таким образом мы можем объединить наши фильтры в цепочку, чтобы получить только нечетные значения квадратов чисел от одного до двадцати:

< ?
class SquareFilter extends FilterIterator {
    public function accept() {
        return true;
    }
    public function current() {
        return pow(parent::current(), 2);
   }
}
foreach (new OddOnlyFilter(new SquareFilter(xrange(1, 20))) as $i) {
    echo $i, ",";
}
// 1,9,25,49,81,121,169,225,289,361,
?>

Вы могли конечно написать при помощи цикла for - но вы создадите достаточно много фильтров, зачем вам это надо? Хотелось бы надеяться, Вы начинаете видеть истинный потенциал объектных диапазонов чисел.

Итераторы - несколько новое, и очень желанное добавление к PHP. До нового ООП интерфейса в PHP 5, они не были даже возможны. Передовые интеллектуальные усилия, например SPL , были сосредоточены на обеспечении универсальных итераторов для материальных структур; каталогов, массивов, других итераторов и так далее. Я не верю, что проводилось большое исследование в отношении использования PHP итераторов на виртуальных структурах, типа дискретных числовых последовательностей, наборов или алгоритмов ... пока.

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

Я использовал SPL SeekableIterator интерфейс и горстку разных функций. При написании моих тестов, я натолкнулся на SPL функцию iterator_to_array(), которая создает массив из значений итератора. Это по существу противоположность того, чего я пытаюсь добиться. Но это все же полезно. Я использую ее, чтобы быстро оценить эквивалентность со встроенной range() функцией. Это работает в большинстве случаев, кроме тех случаев, когда точность значений с плавающей запятой поднимает свою уродливую голову:

< ?
$success = range(0, 10) == iterator_to_array(xrange(0, 10));
?>

Горе улучшения

Вы можете задаваться вопросом, "Не медленнее ли итераторы, чем регулярные циклы?" да, так и есть, но Вы вероятно не будете обращать внимание на различие в скорости. Как в случае с обработкой особых ситуаций, и даже объектов, итераторы получили плохую репутацию от преждевременных энтузиастов оптимизации. Есть хороший шанс, что прикладная логика, которую Вы строите, будет намного более сложной чем алгоритмы выполнения цикла, которые Вы решите осуществить. Есть всегда исключения, но это - довольно определенное правило. Учитывая, что ваше приложение вероятно не будет в основном состоять из плотных циклов, воздействие на работу скрипта будет незначительно.

Чтобы добавить некоторый вес моему аргументу, я написал набор бенчмарков для PHPUnit, чтобы сравнить скорость работы стандартного цикла for, итератора, основанного на массиве, и моего класса XRangeIterator (написанный и в PHP и в C). Тесты были запущены более чем на час, что превышает способности профайлера XDebug, поэтому мне пришлось писать свой набор бенчмарков для измерения производительности. Каждый тест запускался три раза и усреднялся. Стандартная погрешность подсчета времени исполнения для каждого теста не превосходила 0.01 (обычно), что подходит для этого сравнительного анализа. Я сделал мой бенчмарк доступным для скачивания, для тех из Вас, кто любопытен. В Иллюстрации 1, Вы можете видеть граф результатов на моей собственной машине.

Бенчмарк показывает, что цикл for() является всегда самым быстрым. Производительность range() варьируется, но вообще она меньше не менее чем в пять раз стандартного for() цикла. XRangeIterator, написанный в PHP, еще медленнее. Он медленнее более чем в 22 раза по сравнению с обычными циклами. Это может казаться обескураживающим, пока Вы не поймете, что это - все еще невероятно быстро... действительно. Наш самый медленный итератор тратит 0.0000068 секунды в итерацию. Если Вы не используете итераторы в больших циклах, Вы вряд ли даже обратите внимание на различие в работе.

В самом плохом случае, XRangeIterator может достигнуть приблизительно 150 итераций в миллисекунду на медленных аппаратных средствах. Используя этот бенчмарк, можно установить 'правило миллисекунды' и просто отказаться от любого решения, приводящего к более высокому времени исполнения. Я думаю Вы увидите, что в большинстве ситуаций, Вы не будете пропускать дополнительные миллисекунды.

Как PHP разработчик, Вы изучаете маленькие уловки здесь и там, чтобы ускорять то, что иначе выполнялось бы медленно. Многие из тех уловок хорошо знать, но маленькая горстка абсолютно отвратительна. Я намеревался выяснить, насколько быстрее я мог заставить минимальный код функционировать при чистой инсталляции PHP. Использование Zend Optimizer вне рассмотрения, хотя это вероятно улучшило бы результаты. Я хотел кое-что, что выполнится хорошо на всех инсталляциях, но в пределах PHP непосредственно, у меня было мало вариантов.

Я понял, что я был в состоянии сократить время выполнения более чем на 20% делая XRangeIterator абстрактным классом и определение методов current() при помощи eval(). Это избавляет от необходимости читать переменные $low и $step. Однако, это не такие хорошие новости. Исполнение кода может быть быстрее когда переменные заменены на литералы, но 'усовершенствования' соседствуют с платой зависящей от eval() кода и очень замысловатого выполнения. Как бы я не хотел улучшить производительность, это не тот способ, которым стоило бы воспользоваться. Мы обменяли бы один плохой способ исполнения на другой плохой способа исполнения. Чистая выгода: ноль.

Больше производительности

Принимая во внимание, что я использую только foreach(), я не хотел принимать довольно неутешительные результаты для итераторов. Сырая производительность не беспокоила меня настолько, насколько принцип вопроса. Для получения максимальной производительности от моих итераторов я должен был использовать С. Так как класс XRangeIterator является результирующим кодом, это - хороший кандидат для расширения, таким образом то, что я сделал - это сел и написал расширение xrange.

Я использовал код PHP, который я уже написал как справочную реализацию. Дизайн расширения идентичен, но оно выполняется более чем в три раза быстрее. Это возможность для еще 20% или около того, но я решил что действительно узкие места лежат в другом месте. Имей PHP более высокую производительность, я пропустил бы этот шаг полностью. Как побочное примечание, если Вы когда-либо захотите писать расширения для PHP, заранее хочу порекомендовать создание прототипов в PHP.

Насколько далеко зайдет расширение — это единственно важный вопрос. Это использование волшебного метода, исключения, классы и интерфейсы, но это действительно только одина известная возможность. Формула, лежащая в основе всего этого, является основополагающей. Это увеличение шага итерации и добавление результата к меньшему значению. Это — алгоритм в основе current() метода.

Расширение было написано, чтобы функционировать, насколько это возможно, таким же образом как оригинальная версия написанная в PHP; это только быстрее. Есть немного незначительных различий в выполнении. Например, я решил избегать использование свойств в целом, поскольку я не мог найти серьезные основания для того, чтобы делать видимыми приватные значения. Постарался избегать просмотра с хешированием и преобразования типов, но я был все еще немного расточительный. Версия C XrangeIterator использует математику с плавающей запятой и 64-битовое целое число (эмулировал long long), чтобы класс поддерживал ненормально большие циклы. Я провел тесты, и решил, что обмен приемлемый.

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

Я скомпилировал PHP с --enable-maintainer-zts, чтобы гарантировать наличие всех макросов, используемых в xrange. Нет никакой потребности в любых глобальных данных, и я довольно уверен, что все функции повторно используемые. Хоть я и не проверял, в действительно многопоточной среде все должно работать прекрасно. Единственное внешняя зависимость — SPL, но она поставляется с PHP по умолчанию. Расширения могут быть собраны статически и как общедоступные библиотеки, не создавая неприятностей.

Результат всего этого — это два xrange, доступные для Вашего выбора. Если у вас есть возможность установить расширение, то Вы жертвуете очень малой производительностью при использовании xrange. Если у Вас нет возможности установить Ваши собственные расширения, например на общедоступном хостинге, есть код, написанный на PHP в Листинге 1. Оба выполнение достаточно быстры для использования в реальном коде, но расширение все же быстрее. Только на сайте очень большого объема можно увидеть различие в работе между выполнением PHP и C реализациями.

Полный исходный текст xrange PHP расширения может быть найден на SourceForge в https://sourceforge.net/projects/xrange/. Я не упаковал скомпилированные бинарники, поэтому Вы должны уметь компилировать исходные коды PHP, я понимаю, что это может быть проблематичным под Windows. Я приложил усилия, чтобы сделать проект доступным через PECL, и предложение получило немного поддержки, таким образом мы надеемся, что к тому моменту, когда Вы читаете эту статью, проблема будет решенной. И Windows версия будет доступна через http://pecl4win.php.net. Исходный текст для расширения выпущен согласно Лицензии PHP, таким образом она может использоваться свободно как отправная точка, чтобы построить Ваш собственный C-подобный генератор алгоритмов и собственные итераторы.

Заключение

Адаптация Вашего кода для использования итераторов где только это возможно, обеспечит большую гибкость, последовательность и безопасность. Производительность этого подхода почти всегда незначительна. Расширение PHP Вашим собственным итератором, как я продемонстрировал с XRangeIterator, может обеспечить более мощную замену для обычного for() цикл. Комбинируя Ваши итераторы с SPL и другим итераторами в конечном счете позволит Вам писать более мощный, и более удобный в сопровождении код.

Об авторе

Пол Чандлер - опытный Web разработчик с более чем четырнадцатью годами опыта разработки в Интернет. Он имеет сертифекаты Zend (ZCE) и в PHP 4 и в PHP 5. В течение дня, он работает как старший инженер для крупной сети доставки контента в сети Интернет. Ночью, он работает над совершенно секретным кодом и анонимно вносит свой вклад в разнообразие открытых проектов. Как независимый подрядчик, он увел многочисленные крупномасштабные перемещения PHP от конкурирующих технологий, типа .Net и J2EE. В его свободном времени, он любит прыгать с хороших аэропланов.

О переводчике

Колесников Евгений - программист компании Twin px. Опыт Web разработок около 2-х лет

Приложение: Листинг 1

< ?php
/* vim: set ts=4 sw=4: */

assert('extension_loaded("SPL") || !extension_loaded("xrange");');

/**
 * Provides range() equivalent (numeric) iterator without the use of arrays
 * @author Paul Chandler
<[email protected]>
 * @package CustomIterators
 * @throws InvalidArgumentException All parameters must be numeric
 * @throws OutOfBoundsException Invalid seek position
 * @see XRangeIteratorTest.php Unit Test Cases
 */
class XRangeIterator implements SeekableIterator {
    /**
     * @var mixed First value
     */
    private $low;

    /**
     * @var mixed Last value
     */
    private $high;

    /**
     * @var mixed Iteration increment
     */
    private $step;

    /**
     * @var int Current iteration count
     */
    private $iteration;

    /**
     * @var int Maximum number of iterations
     */
    private $iterations;

    /**
     * @var string Custom evaluator function
     */
    private $evaluator;

    /**
     * Constructs XRangeIterator
     * @return XRangeIterator Initialized iterator
     * @param int|float $low First value
     * @param int|float $high Last value
     * @param int|float $step Iteration increment / decrement
     * @throws InvalidArgumentException All parameters must be numeric
     */
    public function __construct($low, $high, $step = 1)
    {
        if (
            !is_numeric($low)
            || !is_numeric($high)
            || !is_numeric($step)
        ) throw new InvalidArgumentException(
            'All parameters must be numeric'
        );

        if ( // flip the step if backward
            ($high < $low &#038;& $step > 0) || ($high > $low &#038;& $step < 0)
        ) $step *= -1;

        $this->low  = $low;
        $this->high = $high;
        $this->step = $step;
        $this->iterations = floor(abs(($high - $low) / $step));
        $this->iteration  = 0;
    }

######################################################################
# SeekableIterator interface methods
######################################################################

    /**
     * Returns the current value
     * @return int|float Current value
     */
    public function current()
    {
        return $this->low + $this->step * $this->iteration;
    }

    /**
     * Returns the current key
     * @return int Current key
     */
    public function key()
    {
        return $this->iteration;
    }

    /**
     * Next iteration
     */
    public function next()
    {
        ++$this->iteration;
    }

    /**
     * Rewinds to start
     */
    public function rewind()
    {
        $this->iteration = 0;
    }

    /**
     * Evaluates if the current value is valid
     * @return bool Valid?
     */
    public function valid()
    {
        return $this->iteration < = $this->iterations;
    }

    /**
     * Seeks to an arbitrary position
     * @param int $position Seek value
     * @throws OutOfBoundsException Invalid seek position
     */
    public function seek($position)
    {
        if ($position < 0 || $position > $this->iterations)
            throw new OutOfBoundsException('Invalid seek position');
        else
            $this->iteration = $position;
    }
}

######################################################################
# xrange() function // XRangeIterator factory
######################################################################

/**
 * Provides a range() compatible functional wrapper for XRangeIterator
 * @return XRangeIterator Initialized iterator
 * @param int|float $low First value
 * @param int|float $high Last value
 * @param int|float $step Iteration increment / decrement
 */
function xrange($low, $high, $step = 1)
{
    return new XRangeIterator($low, $high, $step);
}

?>




Постоянные ссылки

При копировании ссылка на TeaM RSN обязательна!

URI

Html (ЖЖ)

BB-код (Для форумов)

Оставить комментарий

Вы должны войти, чтобы оставить комментарий.