class Seq
class Seq is Cool does Iterable does Sequence { }
A Seq
represents anything that can produce a sequence of values. A
Seq
is born in a state where iterating it will consume the values.
Calling .cache
on a Seq
will make it store the generated values
for later access.
A high-level construct to generate a Seq
is gather/take,
as well as many built-in methods like map and
grep, low-level constructors to create a
Seq
from an iterator or from looping constructs are available too.
A Seq
can also be constructed with the
sequence operator ... or one of its
variants.
my $s = (1...5);
say $s; # OUTPUT: Ā«(1 2 3 4 5)ā¤Ā»
say $s.^name; # OUTPUT: Ā«Seqā¤Ā»
Assigning the values of a Seq
to an array consumes a Seq
that is
not lazy. Use the lazy
statement prefix to avoid a Seq
from being
iterated during the assignment:
# The Seq created by gather ... take is consumed on the spot here.
my @a = gather do { say 'consuming...'; take 'one' }; # OUTPUT: Ā«consuming...ā¤Ā»
# The Seq here is only consumed as we iterate over @a later.
my @a = lazy gather do { say 'consuming...'; take 'one' }; # outputs nothing.
.say for @a; # OUTPUT: Ā«consuming...ā¤oneā¤Ā»
A typical use case is method lines in IO::Handle, which could use a lot of memory if it stored all the lines read from the file. So
for open('README.md').lines -> $line {
say $line;
}
won't keep all lines from the file in memory.
This implies that you cannot iterate the same Seq
object twice (otherwise
it couldn't throw away old values), so this dies:
my @a = 1, 2, 3;
my @b = <a b c>;
my \c = @a Z=> @b;
.say for c;
.say for c; # fails
CATCH { default { put .^name, ': ', .Str } };
# OUTPUT: Ā«X::Seq::Consumed: This Seq has already been iterated, and its values consumed
# (you might solve this by adding .cache on usages of the Seq, or
# by assigning the Seq into an array)Ā»
Caution: No program should ever assume a Seq
may only be iterated once
even if not cached by the program. Caching is a volatile state exposed to the
developer as an optimization. The Seq
may become cached by many operations,
including calling .raku
(.perl
before version 2019.11) on the Seq
(if
called prior to a non-cached iteration). From version 6.d, .raku
(again,
.perl
before version 2019.11) can be called on consumed Seq
. If a program
assumes a Seq
can only iterate once, but then is later changed to call one of
these operations during the loop, that assumption will fail.
On a cached Seq
, the cached list is used when &infix:<eqv>
, .Slip
,
.join
, .List
, .list
, .eager
, .Array
and .is-lazy
are called.
You can smartmatch a regex with Seq
, even if it's infinite
my @fib = 1,1, *+* ... *;
say @fib[^1000] ~~ /^9999/; # OUTPUT: Ā«Nilā¤Ā»
However, infinite or lazy Seq
will be vivified when doing the match,
leading to possibly infinite loops, so be sure to limit search somehow.
Methods
method new
proto method new(Seq: |) {*}
multi method new(Seq: Iterator:D $iter)
multi method new(Seq:)
Creates a new Seq
object from the supplied iterator passed as the single
argument. Creates an empty Seq
if called with no argument.
method iterator
method iterator(Seq:D:)
If the Seq
is not cached, returns the underlying iterator and marks
the invocant as consumed. If called on an already consumed sequence,
throws an error of type X::Seq::Consumed.
Otherwise returns an iterator over the cached list.
method is-lazy
method is-lazy(Seq:D:)
Returns True
if and only if the underlying iterator or cached list
considers itself lazy. If called on an already consumed sequence, throws
an error of type X::Seq::Consumed.
method Seq
multi method Seq(Seq:D:)
Clones the object.
method Capture
method Capture()
Coerces the object to a List, which is in turn coerced into a Capture.
method elems
method elems(Seq:D:)
Returns the number of values in the sequence. If this number cannot be
predicted, the Seq
is cached and evaluated till the end.
Because an infinite sequence cannot be evaluated till the end, such a
sequence should be declared lazy. Calling .elems
on a lazy Seq
fails with X::Cannot::Lazy.
method from-loop
multi method from-loop(&body, :$label)
multi method from-loop(&body, &cond, :$repeat!, :$label)
multi method from-loop(&body, &cond, :$label)
multi method from-loop(&body, &cond, &afterwards, :$label)
These methods create new Seq
-based callbacks.
In general, it produces an infinite Seq
by calling &body
each time a new
element is requested, using the return value from &body
as the item. This
emulates (or implements) a loop { body }
construct.
When the multi includes &cond
, it's invoked before each call to
&body
, and terminates
the sequence if &cond
returns a false value. If $repeat
is set to a true
value, the first call to &cond
is omitted, and &body
called right away.
This emulates (or implements) while cond { body }
and
repeat { body } while cond
loops.
If present, &afterward
will be called after each call to &body
.
method sink
method sink(--> Nil)
Calls sink-all if it is an Iterator, sink
if the Sequence is a list.
say (1 ... 1000).sink; # OUTPUT: Ā«Nilā¤Ā»
This is something you might want to do for the side effects of producing those values.
method skip
multi method skip(Seq:D:)
multi method skip(Seq:D: Whatever)
multi method skip(Seq:D: Callable:D $w)
multi method skip(Seq:D: Int() $n)
multi method skip(Seq:D: $skip, $produce)
Returns a Seq containing whatever is left of the invocant after
throwing away $n
of the next available values. Negative values of
$n
count as 0. Also can take a WhateverCode to indicate how many values
to skip from the end. Will block on lazy Seqs until the requested
number of values have been discarded.
say (1..5).Seq.skip; # OUTPUT: Ā«(2 3 4 5)ā¤Ā»
say (1..5).Seq.skip(3); # OUTPUT: Ā«(4 5)ā¤Ā»
say (1..5).Seq.skip(5); # OUTPUT: Ā«()ā¤Ā»
say (1..5).Seq.skip(-1); # OUTPUT: Ā«(1 2 3 4 5)ā¤Ā»
Calling it with Whatever will return an empty Seq
:
say <1 2 3>.Seq.skip(*); # OUTPUT: Ā«()ā¤Ā»
The multi that uses a Callable is intended mainly to be used this way:
say (1..5).Seq.skip(*-3); # OUTPUT: Ā«(3 4 5)ā¤Ā»
Instead of throwing away the first $n
elements, it throws away everything
but the elements indicated by the WhateverCode, in this case all but the
last three elements.
As of language version 6.e
(early implementation exists in Rakudo
compiler 2022.12+), it is also possible to specify multiple argument values.
These are then interpreted as number of values to produce, then skip, then
produce, etc.
say (1..12).Seq.skip(2,3,4); # OUTPUT: Ā«(3 4 5 10 11 12)ā¤Ā»
This first skipped 2 values, then produced 3 values (3, 4, 5), skipped 4 values and then produced the rest (10, 11, 12).
If the final value specified is in a "produce" position, then the rest of the
Seq
will be skipped. If the final value is in a "skip" position, then the
rest of the Seq
will be produced.
say (1..10).Seq.skip(2,3,1,2); # OUTPUT: Ā«(3 4 5 7 8)ā¤Ā»
say (1..10).Seq.skip(2,3,1); # OUTPUT: Ā«(3 4 5 7 8 9 10)ā¤Ā»
If a Whatever is specified in a "produce" position, it will cause the rest
of the Seq
to be produced. Otherwise, it will cause the rest if the Seq
to be skipped.
say (1..10).Seq.skip(2,*); # OUTPUT: Ā«(3 4 5 6 7 8 9 10)ā¤Ā»
say (1..10).Seq.skip(2,3,*); # OUTPUT: Ā«(3 4 5)ā¤Ā»
If you want to start with producing values instead of skipping, specify 0 as the first value.
say (1..10).Seq.skip(0,3,4); # OUTPUT: Ā«(1 2 3 8 9 10)ā¤Ā»
If you want an unending repeating pattern of skips and produces, you can
specify the arguments as an unending Seq
themselves.
say (^20).Seq.skip(|(2,3) xx *); # OUTPUT: Ā«(0 1 5 6 10 11 15 16)ā¤Ā»
multi method slice
method slice(Seq:D: *@indices --> Seq:D)
Available as of the 2021.02 release of the Rakudo compiler.
The slice
method takes a number of monotonically increasing indices for
which to produce the value from the invocant in a new Seq
. Indices can
be single numbers or ranges, as long they are increasing in value.
This provides a more efficient way of getting specific values out of a
Seq
than either caching the Seq
or converting it to a List or
Array.
say (1..10).Seq.slice(0, 3..6, 8); # OUTPUT: Ā«(1 4 5 6 7 9)ā¤Ā»