Conditional Independence Net

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:

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.