Итераторы

Итераторы в NewLang, как и в остальных языках программирования, предназначены для перебора элементов. Но, в отличии от итераторов в С++, итераторы NewLang являются самостоятельными объектами, а не указателями на отдельные элементы объекта-контейнера. Итераторы NewLang поддерживают фильтрацию элементов по имени за счет использования функций обратного вызова.

Для работы с итераторами используется следующий синтаксис:

  • ? или ?( текст ) - создание итератора без фильтра или с regex фильтрацией по имени поля
  • ?( func, args… ) - создание итератора с использованием лямбда функции или функции обратного вызова
  • ! или !( количество возвращаемых элементов ) - перебор элементов итератора
  • ?! или !? - получить текущий элемент без перемещение курсора
  • !! - сбросить указатель итератора в начальное состояние (на первый элемент)
  • ?? - создать итератор и сразу его выполнить, возвращая все значения в виде элементов словаря максимально возможного размера. Это своего рода синтаксический сахар для краткой записи последовательности команд ?; !(9223372036854775807);, что удобно использовать при отладке для вывода значений переменных.

Для работы с итераторами можно использовании DSL операторы:

  • iter() - Создать итератор (?)
  • next() - перебор элементов итератора (! или !( количество возвращаемых элементов ))
  • curr() - Получить текущий элемент без перемещение курсора (?! или !?)
  • first() - Сбросить указатель итератора на первый элемент (!!)
  • all() - Создать итератор и сразу его выполнить (??)

Примеры создания итераторов с разными фильтрами отбора данных:

iter := dict ? ("name"); # Создание итератора для значений с указанным именем
iter := dict ? ("regex."); # Создание итератора для полей с префиксом "regex"

# Чистая функция для фильтрации по значению
filter(value) :- { $value && $value < 10; }; 
iter := dict ? (filter); # Создание итератора для значений меньше 10

equal(value, arg) := { $value == arg }; # Обычная функция
iter := dict ? (equal, 100); # Создать итератор только для значений 100

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

Если указать количество считываемых элементов !(0), то будет возвращен сам элемент, но для значений отличных от нуля будет возвращаться не элемент данных, а словарь с данными, считанными из итератора. Из-за этого операторы ! и !(0) НЕ эквивалентны, т.к. по разному обрабатывают конец данных.

Лучше всего это показать на примерах для словаря с пятью элементами:

dict := (1,2,3,4,5,)?; # Создать итератор для словаря

dict!; # -> 1
dict!; # -> 2
dict!; # -> 3
dict!; # -> 4
dict!; # -> 5
dict!; # -> будет исключение "конец итератора"

# Но
dict !(0); # -> (1,)
dict !(0); # -> (2,)
dict !(0); # -> (3,)
dict !(0); # -> (4,)
dict !(0); # -> (5,)
dict !(0); # -> (,) - вернется пустой словарь

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

dict := (1,2,3,4,5,) ?; # Итератор для словаря с пятью элементами

dict !(3); # -> (1,2,3,)
dict !(3); # -> (4,5,)
dict !(3); # -> (,)

# Но
dict !(-3); # -> (1,2,3,)
dict !(-3); # -> (4,5, :IteratorEnd)
dict !(-3); # -> (:IteratorEnd, :IteratorEnd, :IteratorEnd)