Rules / Types, classes & roles / composite

Iterator Reference

Generic API for producing a sequence of values

What it is #

role to an array, for instance, like in this example:

Position in the hierarchy #

Inherits from
Does
Consumed byPredictiveIterator

Routines #

Signatures are reproduced from the documentation, including their declared return types. A routine listed here is part of the type's published interface; whether Raku++ implements it is a separate question, answered by the examples below.

method pull-one #

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

Returns Mu.

This method stub ensures that classes implementing the Iterator role provide a method named pull-one. The pull-one method is supposed to produce and return the next value if possible, or return the sentinel value IterationEnd if no more values could be produced. As a more illustrative example of its use, here is a count down iterator along with a simplistic subroutine re-implementation of the for loop.

method push-exactly #

method push-exactly(Iterator:D: $target, int $count --> Mu)

Returns Mu.

Should produce $count elements, and for each of them, call $target.push($value). If fewer than $count elements are available from the iterator, it should return the sentinel value IterationEnd. Otherwise it should return $count. The Iterator role implements this method in terms of pull-one. In general, this is a method that is not intended to be called directly from the end user who, instead, should implement it in classes that mix the iterator role. For

method push-at-least #

method push-at-least(Iterator:D: $target, int $count --> Mu)

Returns Mu.

Should produce at least $count elements, and for each of them, call $target.push($value). If fewer than $count elements are available from the iterator, it should return the sentinel value IterationEnd. Otherwise it should return $count. Iterators with side effects should produce exactly $count elements; iterators without side effects (such as Range iterators) can

method push-all #

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

Should produce all elements from the iterator and push them to $target. The Iterator role implements this method in terms of push-at-least. As in the case of the other push-* methods, it is mainly intended for developers implementing this role. push-all is called when assigning an object with this role to an array, for instance, like in this example: The push-all method implemented pushes to the target iterator in lists of

method push-until-lazy #

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

Returns Mu.

Should produce values until it considers itself to be lazy, and push them onto $target. The Iterator role implements this method as a no-op if is-lazy returns a True value, or as a synonym of push-all if not. This matters mostly for iterators that have other iterators embedded, some of which might be lazy, while others aren't.

method is-deterministic #

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

Returns Bool:D.

Should return True for iterators that, given a source, will always produce the values in the same order. Built-in operations can perform certain optimizations when it is certain that values will always be produced in the same order. Some examples: The Iterator role implements this method returning True, indicating an iterator that will always produce values in the same order.

method is-monotonically-increasing #

method is-monotonically-increasing(Iterator:D: --> Bool:D)

Returns Bool:D.

Should return True for iterators that, given a source, will always produce the values in the increasing value (according to cmp semantics). Built-in operations can perform certain optimizations when it is certain that values will always be produced in ascending order. Some examples: The Iterator role implements this method returning False, indicating an iterator that will not produce values with increasing values.

method is-lazy #

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

Returns Bool:D.

Should return 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(*). The Iterator role implements this method returning False, indicating a non-lazy iterator.

method sink-all #

method sink-all(Iterator:D: --> IterationEnd)

Returns IterationEnd.

Should exhaust the iterator purely for the side-effects of producing the values, without actually saving them in any way. Should always return IterationEnd. If there are no side-effects associated with producing a value, then it can be implemented by a consuming class to be a virtual no-op. The Iterator role implements this method as a loop that calls pull-one until it is exhausted.

method skip-one #

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

Returns Mu.

Should skip producing one value. The return value should be truthy if the skip was successful and falsy if there were no values to be skipped: The Iterator role implements this method as a call pull-one and returning whether the value obtained was not IterationEnd.

method skip-at-least #

method skip-at-least(Iterator:D: $target, int $to-skip --> Mu)

Returns Mu.

Should skip producing $to-skip values. The return value should be truthy if the skip was successful and falsy if there were not enough values to be skipped: The Iterator role implements this method as a loop calling skip-one and returning whether it returned a truthy value sufficient number of times.

method skip-at-least-pull-one #

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

Returns Mu.

Should skip producing $to-skip values and if the iterator is still not exhausted, produce and return the next value. Should return IterationEnd if the iterator got exhausted at any point: The Iterator role implements this method as calling skip-at-least and then calling pull-one if it was not exhausted yet. Please see the PredictiveIterator role if your Iterator can know

Examples, run three ways #

Every example below comes from the official documentation, together with the output that documentation asserts. Each was then executed by Rakudo and by Raku++ when this page was built. Where the three agree, one result is shown; where they do not, all three are — because which of them is wrong is exactly the information worth having.

2 all-differ · 11 no-output · 2 not-runnable · 11 ok · 3 rakupp-differs

constant IterationEnd
role Iterator { }

Not executed: the documentation states no expected output for this example.

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: 1, Any, 3, Int, 5, Mu, 7;

for @a -> $a, $b {
say [$a, $b];
}

# OUTPUT: «[1 3]␤[5 7]␤»
Output
[1 3]
[5 7]

Documentation, Rakudo and Raku++ all agree.

.say for ["foo",IterationEnd, "baz"]; # OUTPUT: «foo␤»
say ["foo",IterationEnd, "baz"].map: "«" ~ * ~ "»";
# OUTPUT: «(«foo» «IterationEnd» «baz»)␤»
Output
foo
(«foo» «IterationEnd» «baz»)

Documentation, Rakudo and Raku++ all agree.

my $it = (1,2).iterator;
$it.pull-one for ^2;
say $it.pull-one =:= IterationEnd; # OUTPUT: «True␤»
Output
True

Documentation, Rakudo and Raku++ all agree.

my $it = (1,2).iterator;
$it.pull-one for ^2;
my $is-it-the-end = $it.pull-one;
say $is-it-the-end =:= IterationEnd; # OUTPUT: «False␤»
Documentation
False
Rakudo
False
Raku++
True

Raku++ disagrees with both the documentation and Rakudo — a defect.

my $is-it-the-end := $it.pull-one;
say $is-it-the-end =:= IterationEnd; # OUTPUT: «True␤»

Neither engine can run this in isolation — the example depends on context from the surrounding text.

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.raku;  # OUTPUT: «IterationEnd␤»
Output
1
2
3
IterationEnd

Documentation, Rakudo and Raku++ all agree.

# works the same as (10 ... 1, 'lift off')
class CountDown does Iterator {
    has Int:D $!current = 10;

Not executed: the documentation states no expected output for this example.

    method pull-one ( --> Mu ) {
        my $result = $!current--;
        if $result ==  0 { return 'lift off' }
        if $result == -1 { return IterationEnd }

Not executed: the documentation states no expected output for this example.

        # calling .pull-one again after it returns IterationEnd is undefined
        if $result <= -2 {
            # so for fun we will give them nonsense data
            return (1..10).pick;
        }

Not executed: the documentation states no expected output for this example.

        return $result;
    }
}

Not executed: the documentation states no expected output for this example.

sub for( Iterable:D $sequence, &do --> Nil ) {
    my Iterator:D $iterator = $sequence.iterator;

Not executed: the documentation states no expected output for this example.

    loop {
        # must bind the result so that =:= works
        my Mu $pulled := $iterator.pull-one;

Not executed: the documentation states no expected output for this example.

        # always check the result and make sure that .pull-one
        # is not called again after it returns IterationEnd
        if $pulled =:= IterationEnd { last }

Not executed: the documentation states no expected output for this example.

        do( $pulled );
    }
}

Not executed: the documentation states no expected output for this example.

for( Seq.new(CountDown.new), &say );  # OUTPUT: «10␤9␤8␤7␤6␤5␤4␤3␤2␤1␤lift off␤»

Neither engine can run this in isolation — the example depends on context from the surrounding text.

until IterationEnd =:= (my \pulled = $iterator.pull-one) {
do( pulled );
}

Not executed: the documentation states no expected output for this example.

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

Documentation, Rakudo and Raku++ all agree.

class DNA does Iterable does Iterator {
has $.chain;
has Int $!index = 0;

method new ($chain where {
                   $chain ~~ /^^ <[ACGT]>+ $$ / and
                   $chain.chars %% 3 } ) {
    self.bless( :$chain );
}

method iterator( ){ self }

method pull-one( --> Mu){
  if $!index < $.chain.chars {
     my $codon = $.chain.comb.rotor(3)[$!index div 3];
     $!index += 3;
     return $codon;
  } else {
    return IterationEnd;
  }
}

method push-exactly(Iterator:D: $target, int $count --> Mu) {
    return IterationEnd if $.chain.elems / 3 < $count;
    for ^($count) {
        $target.push: $.chain.comb.rotor(3)[ $_ ];
    }
}

};

my $b := DNA.new("AAGCCT");
for $b -> $a, $b, $c { say "Never mind" }; # Does not enter the loop
my $þor := DNA.new("CAGCGGAAGCCT");
for $þor -> $first, $second {
say "Coupled codons: $first, $second";
# OUTPUT: «Coupled codons: C A G, C G G␤Coupled codons: A A G, C C T␤»
}
Documentation
Coupled codons: C A G, C G G
Coupled codons: A A G, C C T
Rakudo
Raku++
Never mind
Coupled codons: C A G C G G A A G C C T, 

All three differ. Needs a human.

Not yet examined. Which of these is correct has not been established — do not treat either engine as settled here.

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

Documentation, Rakudo and Raku++ all agree.

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

Not executed: the documentation states no expected output for this example.

class DNA does Iterable does Iterator {
has $.chain;
has Int $!index = 0;

method new ($chain where {
                   $chain ~~ /^^ <[ACGT]>+ $$ / and
                   $chain.chars %% 3 } ) {
    self.bless( :$chain );
}

method iterator( ){ self }
method pull-one( --> Mu){
  if $!index < $.chain.chars {
     my $codon = $.chain.comb.rotor(3)[$!index div 3];
     $!index += 3;
     return $codon;
  } else {
    return IterationEnd;
  }
}

method push-all(Iterator:D: $target) {
    for $.chain.comb.rotor(3) -> $codon {
        $target.push: $codon;
    }
}

};

my $b := DNA.new("AAGCCT");
my @dna-array = $b;
say @dna-array; # OUTPUT: «[(A A G) (C C T)]␤»
Documentation
[(A A G) (C C T)]
Rakudo
[(A A G) (C C T) (A A G) (C C T)]
Raku++
[DNA<obj>]

All three differ. Needs a human.

Not yet examined. Which of these is correct has not been established — do not treat either engine as settled here.

say (1..10).iterator.is-deterministic;              # OUTPUT: «True␤»
say (1..10).roll(5).iterator.is-deterministic;      # OUTPUT: «False␤»
say %(a => 42, b => 137).iterator.is-deterministic; # OUTPUT: «False␤»
Documentation
True
False
False
Rakudo
True
False
False
Raku++
True
True
False

Raku++ disagrees with both the documentation and Rakudo — a defect.

say (1..10).iterator.is-monotonically-increasing;              # OUTPUT: «True␤»
say (1..10).roll(5).iterator.is-monotonically-increasing;      # OUTPUT: «False␤»
say %(a => 42, b => 137).iterator.is-monotonically-increasing; # OUTPUT: «False␤»
Documentation
True
False
False
Rakudo
True
False
False
Raku++
True
True
False

Raku++ disagrees with both the documentation and Rakudo — a defect.

say (1 .. 100).iterator.is-lazy; # OUTPUT: «False␤»
say (1 .. ∞).iterator.is-lazy; # OUTPUT: «True␤»
Output
False
True

Documentation, Rakudo and Raku++ all agree.

say (1 .. 1000).iterator.sink-all;  # OUTPUT: «IterationEnd␤»
Output
IterationEnd

Documentation, Rakudo and Raku++ all agree.

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

Documentation, Rakudo and Raku++ all agree.

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

Documentation, Rakudo and Raku++ all agree.

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␤»
Output
c
True

Documentation, Rakudo and Raku++ all agree.