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 by | PredictiveIterator |
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]»[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»)»
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»
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»
False
False
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»
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: «10987654321lift 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]»
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 GCoupled codons: A A G, C C T»
}Coupled codons: C A G, C G G
Coupled codons: A A G, C C T
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]»
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)]»[(A A G) (C C T)]
[(A A G) (C C T) (A A G) (C C T)]
[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»
True
False
False
True
False
False
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»
True
False
False
True
False
False
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»
False
True
Documentation, Rakudo and Raku++ all agree.
say (1 .. 1000).iterator.sink-all; # OUTPUT: «IterationEnd»
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: «1b0»
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: «1c0»
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: «cTrue»
c
True
Documentation, Rakudo and Raku++ all agree.