role Iterator

Generic API for producing a sequence of values

constant IterationEnd
role Iterator { }

A Iterator is an object that can generate or provide elements of a sequence. Users usually don't have to care about iterators, their usage is hidden behind iteration APIs such as for @list { }, map, grep and list indexing with .[$idx].

The main API is the pull-one method, which either returns the next value, or the sentinel value IterationEnd if no more elements are available. Each class implementing Iterator must provide a pull-one method. All other Iterator API methods can be implemented in terms of pull-one, but also overridden for performance reasons.

IterationEnd

Iterators only allow one iteration over the entire sequence. It's forbidden to make attempts to fetch more data, once IterationEnd has been generated, and behavior for doing so is undefined. For example, the following Seq will not cause the die to be called under normal use, because pull-one will never be called after it returns IterationEnd:

class SkippingArray is Array {
    # skip all undefined values while iterating 
    method iterator {
        class :: does Iterator {
            has $.index is rw = 0;
            has $.array is required;
            method pull-one {
                $.index++ while !$.array.AT-POS($.index).defined && $.array.elems > $.index;
                $.array.elems > $.index ?? $.array.AT-POS($.index++!! IterationEnd
            }
        }.new(array => self)
    }
}
 
my @a := SkippingArray.new;
 
@a.append: 1Any3Int5Mu7;
 
for @a -> $a$b {
    say [$a$b];
};
 
# OUTPUT: «[1 3]␤[5 8]␤» 

The only valid use of the sentinel value IterationEnd in a program is identity comparison (using =:=) with the result of a method in the iterator API. Any other behavior is undefined and implementation dependent.

Methods

method pull-one

Defined as:

method pull-one(Iterator:D: --> Mu)

This method stub ensures that classes implementing the Iterator role provide a method named pull-one.

The pull-one method is supposed to return the next value if available, or the sentinel value IterationEnd if no more elements are available.

my $i = (1 .. 3).iterator;
say $i.pull-one;       # OUTPUT: «1␤» 
say $i.pull-one;       # OUTPUT: «2␤» 
say $i.pull-one;       # OUTPUT: «3␤» 
say $i.pull-one.perl;  # OUTPUT: «IterationEnd␤» 

method push-exactly

Defined as:

method push-exactly(Iterator:D: $targetint $count --> Mu)

Produces $count elements, and for each of them, calls $target.push($value).

If fewer than $count elements are available from the iterator, it returns the sentinel value IterationEnd. Otherwise it returns $count.

my @array;
say (1 .. Inf).iterator.push-exactly(@array3); # OUTPUT: «3␤» 
say @array# OUTPUT: «[1 2 3]␤» 

method push-at-least

Defined as:

method push-at-least(Iterator:D: $targetint $count --> Mu)

Produces at least $count elements, and for each of them, calls $target.push($value).

If fewer than $count elements are available from the iterator, it returns the sentinel value IterationEnd. Otherwise it returns $count.

Iterators with side effects should produce exactly $count elements; iterators without side effects (such as Range iterators) can produce more elements to achieve better performance.

my @array;
say (1 .. Inf).iterator.push-at-least(@array10); # OUTPUT: «10␤» 
say @array# OUTPUT: «[1 2 3 4 5 6 7 8 9 10]␤» 

method push-all

Defined as:

method push-all(Iterator:D: $target)

Produces all elements from the iterator and pushes them to $target.

The fallback is implemented in terms of repeated push-at-least with a large $count.

my @array;
say (1 .. 1000).iterator.push-all(@array); # All 1000 values are pushed 

method push-until-lazy

Defined as:

method push-until-lazy(Iterator:D: $target --> Mu)

Produces values until it considers itself to be lazy, and pushes them onto $target.

This matters mostly for iterators that have other iterators embedded, some of which might be lazy, while others aren't.

method is-lazy

Defined as:

method is-lazy(Iterator:D: --> Bool:D)

Returns True for iterators that consider themselves lazy, and False otherwise.

Built-in operations that know that they can produce infinitely many values return True here, for example (1..6).roll(*).

say (1 .. 100).is-lazy# OUTPUT: «False␤» 
say (1 .. Inf).is-lazy# OUTPUT: «True␤» 

method sink-all

Defined as:

method sink-all(Iterator:D:)

Exhausts the iterator (while discarding generated elements) purely for the side effects of the iteration.

say (1 .. 1000).iterator.sink-all;

method skip-one

Defined as:

method skip-one(Iterator:D: $target --> Mu)

Skips one value. The return value is truthy if skip was successful and falsy if there were no values to skip:

my $i = <a b>.iterator;
say $i.skip-onesay $i.pull-onesay $i.skip-one
# OUTPUT: «1␤b␤0␤» 

method skip-at-least

Defined as:

method skip-at-least(Iterator:D: $targetint $to-skip --> Mu)

Skips $to-skip values. The return value is truthy if skip was successful and falsy if there were not enough values to skip:

my $i = <a b c>.iterator;
say $i.skip-at-least(2); say $i.pull-onesay $i.skip-at-least(20);
# OUTPUT: «1␤c␤0␤» 

method skip-at-least-pull-one

Defined as:

method skip-at-least-pull-one(Iterator:D: $targetint $to-skip --> Mu)

Skips $to-skip values and pulls the next value. The returns the pulled value or IterationEnd if there were not enough values:

my $i = <a b c>.iterator;
say $i.skip-at-least-pull-one(2);
say $i.skip-at-least-pull-one(20=:= IterationEnd;
# OUTPUT: «c␤True␤» 

method count-only

By default is not implemented, but expected implementation for types that do this role is:

method count-only(--> Int:D{ ... }

The method returns the number of elements the Iterator can still produce. Important: it's expected the Iterators that implement this method can produce that number without exhausting themselves. In other words, it's expected the user of the class will be able to still pull-one after calling this method, and eventually receive as many items as the return value of this method indicated.

method bool-only

By default is not implemented, but expected implementation for types that do this role is:

method bool-only(--> Bool:D{ ... }

The method returns True if there are elements that can be generated by this Iterator by, for example, calling pull-one, and False otherwise. Important: it's expected the Iterators that implement this method can produce that answer without exhausting themselves. In other words, it's expected the user of the class will be able to still pull-one after calling this method.

Type Graph

Type relations for Iterator
perl6-type-graph Iterator Iterator

Stand-alone image: vector