Итераторы
Итераторы в 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)