NAME
CInet::Seq - Role for lazy sequences of objects
SYNOPSIS
# Implement the role
package My::Seq {
use Role::Tiny::With;
with 'CInet::Seq';
sub next { … }
}
# More methods provided
say My::Seq->new(…)->grep(…)->count;
DESCRIPTION
unidirectional iterator. once exhausted, you can’t go back.
Required method
next
my $elt = $seq->next;
last if not defined $elt;
Return the next element from sequence. This method should block of while
lazily producing the next element. If the sequence is exhausted, it must
return undef
.
Provided methods
The following methods are provided with a default implementation.
Basic data processing
inhabited
my $empty = not $seq->inhabited;
Returns whether the sequence was inhabited. This is the opposite of “exhausted”. It has the side effect of pulling one element out of the sequence to test for definedness.
list
my @elts = $seq->list;
my $count = $seq->list;
Iterate the entire sequence and return a list of all captured elements in list context. In scalar context, only the number of elements is returned. The objects themselves are thrown away immediately to not occupy memory needlessly.
count
my $count = $seq->count;
Return the number of elements in the sequence. The default implementation of this method exhausts the sequence to accumulate the count. It does so in a memory-friendly way, however.
first
my $truthy = $seq->first;
my $elt = $seq->first(\&code);
Return the first element of the sequence for which the coderef evaluates
to a truthy value, or undef
if the sequence is exhausted while looking
for such an element. This is implemented via ->grep(\&code)->next
.
See grep for details about the coderef.
If the coderef is not given, sub{ 1 }
is used, effectively returning
the first unconsumed element of the sequence. As a special case of
grep, this is a relatively costly way of writing ->next
.
Functional primitives
Seq objects support the following basic functional methods:
map
my $map = $seq->map(\&code);
Returns an instance of CInet::Seq::Map wrapping the invocant to provide
a transformed sequence. The given coderef is expected to be unary and map
the elements of the invocant one at a time. It can refer to the current
element either via its first argument or the dynamically scoped $_
.
grep
my $grep = $seq->grep(\&code);
Returns an instance of CInet::Seq::Grep wrapping the invocant to provide
a filtered sequence. The given coderef is expected to be unary and filter
the elements of the invocant one at a time. It can refer to the current
element either via its first argument or the dynamically scoped $_
.
uniq
my $uniq = $seq->uniq;
my $uniq = $seq->uniq(\&stringifier);
Returns an instance of CInet::Seq::Uniq wrapping the invocant to provide
a filtered sequence where only the first object with a given stringification
is forwarded to the consumer. An optional coderef can be given to specify
how to stringify a given element of the source sequence. It can refer to the
element either via its first argument or the dynamically scoped $_
.
The stringifications of elements are used as keys into a hash. This makes it possible to return the next unique element as soon as it is pulled out of the source sequence, at the expense of using potentially much memory. The cache is freed immediately when the source sequence is exhausted.
The default stringification is sub{ "". $_ }
.
reduce
my $product = $seq->reduce(\&code, $id);
Iterates the entire sequence, applying the coderef to the return value of
the last application (initially the identity element $id
) and the next
element from the sequence. The last return value of the coderef is the
value returned by this method. The sequence is completely exhausted.
This operation is also called folding.
The coderef is assumed to be binary, taking the previously produced
value as the first argument and the next sequence element as the second.
Alternatively, it can refer to the dynamically scoped special package
globals $a
and $b
like a sort
function would.
If not specified, the identity element $id
is initialized by pulling
the first element from the sequence before reducing. If the sequence is
not inhabited, undef
is returned immediately.
stringify
my $strseq = $seq->stringify;
my $strseq = $seq->stringify(\&stringifier);
This is a map which by default uses "". $_
to convert all
incoming elements to strings.
Junctions
The following methods collapse the entire sequence into a Boolean value, depending on a junctive mode. They may short-circuit but in the worst case they iterate the whole sequence:
any
my $satisfiable = $seq->any(\&code);
Return whether the coderef evaluates to a truthy value for any of the sequence elements. This calls first internally, so refer to its documentation as well.
This method stops the iteration over $seq
when the first witness
making the coderef truthy is found.
none
my $unsatisfiable = $seq->none(\&code);
Return whether the coderef evaluates to a truthy value for none of the sequence elements. This calls first internally, so refer to its documentation as well.
This method stops the iteration over $seq
when the first witness
making the coderef truthy is found.
all
my $tautology = $seq->all(\&code);
Return whether the coderef evaluates to a truthy value for all of the sequence elements. This calls first internally, so refer to its documentation as well.
This method stops the iteration over $seq
when the first witness
making the coderef falsy is found.
Utilities
sort
my $list = $seq->sort(with => sub{ $a <=> $b });
my $list = $seq->sort(by => sub{ "". $_ }); # the default
Returns a sequence object for iterating the elements of $seq
in ascending
order. The sorting is performed in one of two ways:
- If the
with
argument is specified, it is assumed to be a binary function that can be used withsort
. It is passed two elements of the sequence to compare. It can alternatively use the dynamically scoped special package globals$a
and$b
in the familiar fashion. - Otherwise the elements are stringified and then sorted according to
Sort::Key::Natural’s
natkeysort
. The stringification method can be overridden by passing a coderef as aby
named argument. This coderef can use either its first argument or the dynamically scoped topic$_
.
WARNING: This method decays the sequence into a reified CInet::Seq::List because sorting algorithms, without a priori knowledge of the sequence, need to see all the elements before they can be certain to return the smallest one.
decorate
my $decorated = $seq->decorate(\&decorator);
Returns another sequence which returns for every incoming element $elt
the value [$elt, &code($elt)]
, that is an arrayref of the original
value and whatever is added by the coderef.
This method aids in Schwartzian transforms of a sequence. Its opposite is undecorate.
undecorate
my $tidied = $seq->undecorate;
my $tidied = $seq->undecorate(\&tidier);
This method aids in Schwartzian transforms of a sequence. Its opposite is decorate.
It is just a map with a default coderef of sub { shift->[0] }
assuming that it undecorates a sequence formatted by decorate.
If the incoming elements are decorated value compounds of a different
format, pass your own coderef. It can either refer to its first argument
or the dynamically scoped topic $_
.
Symmetry reduction
modulo
my $mod = $seq->modulo(\@group);
Returns an instance of CInet::Seq::Modulo wrapping the invocant to provide
a filtered sequence where only the first representative of each group orbit
is forwarded to the consumer. The \@group
arrayref is typically one returned
by the CInet::Symmetry subs and contains a permutation representation of
the group action. The elements of the sequence must implement an act
method
which applies such permutations.
All consumed elements and their images under the group are stringified and used as hash keys. This makes it possible to return the next new representative element as soon as it is pulled out of the source sequence, at the expense of using potentially much memory. The cache is freed immediately when the source sequence is exhausted.
Other
description
my $str = $seq->description;
Returns a human-readable description of the object.
AUTHOR
Tobias Boege tobs@taboege.de
COPYRIGHT AND LICENSE
This software is copyright (C) 2020 by Tobias Boege.
This is free software; you can redistribute it and/or modify it under the terms of the Artistic License 2.0.