2.22. Iterator¶
Iterator is an object which can traverse over a sequence, without knowing details of sequence implementation.
Iterator type is defined as follows:
iterator_type ::= iterator < type >
iterator<int> // iterates over integer
iterator<const Foo&> // iterates over Foo by reference
Iterators can be moved, but not copied or cloned.
Iterator can be created via each
function from a range, static array, or dynamic array.
each
functions are unsafe, because iterator does not capture its arguments:
unsafe
var it <- each ( [[int 1;2;3;4]] )
The most straightforward way to traverse an iterator is a for
loop:
for x in it // iterates over contents of 'it'
print("x = {x}\n")
For the reference iterator for
loop will provide reference variable:
var t = [[int 1;2;3;4]]
for x in t // x is int&
x ++ // increases values inside t
Iterators can be created from lambda (see Lambda) or generator (see Generator).
Calling delete
on an iterator will make it sequence out and free its memory:
var it <- each_enum(Numbers one)
delete it // safe to delete
var it <- each_enum(Numbers one)
for x in it
print("x = {x}\n")
delete it // its always safe to delete sequenced out iterator
Loops and iteration functions do it automatically.
2.22.1. builtin iterators¶
Table keys and values iterators can be obtained via keys
and values
functions:
var tab <- {{ "one"=>1; "two"=>2 }}
for k,v in keys(tab),values(tab) // keys(tab) is iterator<string>
print("{k} => {v}\n") // values(tab) is iterator<int&>
Its possible to iterate over each character of the string via each
function:
unsafe
for ch in each("hello,world!") // string iterator is iterator<int>
print("ch = {ch}\n")
Its possible to iterate over each element of the enumeration via each_enum
function:
enum Numbers
one
two
ten = 10
for x in each_enum(Numbers one) // argument is any value from said enumeration
print("x = {x}\n")
2.22.2. builtin iteration functions¶
empty
function checks if iterator is null or already sequenced out:
unsafe
var it <- each ( [[int 1;2;3;4]] )
for x in it
print("x = {x}\n")
verify(empty(it)) // iterator is sequenced out
More complicated iteration patterns may require next
function:
var x : int
while next(it,x) // this is semantically equivalent to the `for x in it`
print("x = {x}\n")
Next can only operate on copyable types.
2.22.3. low level builtin iteration functions¶
_builtin_iterator_first
, _builtin_iterator_next
, and _builtin_iterator_close
address regular lifetime cycle of the iterator.
A semantic equivalent of the for loop can be explicitly written using those operations:
var it <- each(range(0,10))
var i : int
var pi : void?
unsafe
pi = reinterpret<void?> ( addr(i) )
if _builtin_iterator_first(it,pi)
print("i = {i}\n")
while _builtin_iterator_next(it,pi)
print("i = {i}\n")
_builtin_iterator_close(it,pi)
_builtin_iterator_iterate
is one function to rule them all. It acts like all 3 functions above.
On a non-empty iterator it will start with ‘first’,
then proceeded to call next until the sequence is exhausted.
Once the iterator is sequenced out, it will call close:
var it <- each(range(0,10))
var i : int
var pi : void?
unsafe
pi = reinterpret<void?> ( addr(i) )
while _builtin_iterator_iterate(it,pi) // this is equivalent to the example above
print("i = {i}\n")
2.22.4. next implementation details¶
Function next
is implemented as follows:
def next ( it:iterator<auto(TT)>; var value : TT& ) : bool
static_if !typeinfo(can_copy type<TT>)
concept_assert(false, "requires type which can be copied")
static_elif typeinfo(is_ref_value type<TT>)
var pValue : TT - & ?
unsafe
if _builtin_iterator_iterate(it, addr(pValue))
value = *pValue
return true
else
return false
else
unsafe
return _builtin_iterator_iterate(it, addr(value))
Its important to notice, that builtin iteration functions accept pointer instead of reference.