Acme-IEnumerable
view release on metacpan or search on metacpan
lib/Acme/IEnumerable.pm view on Meta::CPAN
package Acme::IEnumerable;
use strict;
use warnings;
use Exporter;
use vars qw{ $VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS };
BEGIN {
@ISA = qw(Exporter);
%EXPORT_TAGS = ( 'all' => [ qw(
) ] );
@EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
@EXPORT = @EXPORT_OK;
$VERSION = '0.6';
};
#####################################################################
#
#####################################################################
package Acme::IEnumerable::List;
use base qw/Acme::IEnumerable/;
use strict;
use warnings;
use v5.10;
use Carp;
sub _create {
bless {
_list => $_[0],
_zero => 0,
_last => scalar(@{ $_[0] }) - 1,
_dir => 1,
_new => $_[1],
}, __PACKAGE__;
}
sub count {
my ($self) = @_;
$self->{_dir} * ($self->{_last} - $self->{_zero}) + 1;
}
sub element_at {
my ($self, $index) = @_;
Carp::cluck unless defined $index;
croak unless $self->count > $index;
my $projected = $self->{_zero} + $index * $self->{_dir};
$self->{_list}->[$projected];
}
sub last {
my ($self) = @_;
croak unless $self->count;
$self->element_at($self->count - 1);
}
sub last_or_default {
my ($self, $default) = @_;
return $default unless $self->count;
$self->element_at($self->count - 1);
}
sub first {
my ($self) = @_;
croak "No elements for 'first'" unless $self->count;
$self->element_at(0);
}
sub first_or_default {
my ($self, $default) = @_;
return $default unless $self->count;
$self->element_at(0);
}
sub from_list {
my $class = shift;
my @list = @_;
return _create \@list, sub {
return sub {
state $index = 0;
return unless $index <= $#list;
return \($list[$index++]);
};
};
}
sub skip {
my ($self, $count) = @_;
return Acme::IEnumerable::_create(sub {
return sub {
state $index = $count;
return unless $index < $self->count;
return \($self->element_at($index++));
};
});
}
sub reverse {
my ($self) = @_;
return $self->to_list unless $self->count;
my $new;
$new = bless {
_list => $self->{_list},
_last => 0,
_zero => scalar(@{ $self->{_list} }) - 1,
_dir => -1,
_new => sub {
return sub {
state $index = 0;
return unless $index < $new->count;
return \($new->element_at($index++));
}
},
}, __PACKAGE__;
}
#####################################################################
#
#####################################################################
# sub find { ... }
# sub find_index { ... }
# sub find_last { ... }
# sub find_last_idex { ... }
# sub exists { ... }
# sub find_all { ... }
# sub binary_search { ... }
# sub index_of { ... }
# sub last_index_of { ... }
1;
#####################################################################
#
#####################################################################
package Acme::IEnumerable::Ordered;
use strict;
use warnings;
use v5.10;
use Carp;
use base qw/Acme::IEnumerable/;
sub _create {
bless {
_key => $_[0],
_sgn => $_[1],
_par => $_[2],
_new => $_[3],
}, __PACKAGE__;
}
sub order_by {
# This assumes to_enumerable will remove the ::Ordered base type
my ($self) = @_;
$self->to_enumerable->order_by(@_);
}
sub order_by_descending {
# This assumes to_enumerable will remove the ::Ordered base type
my ($self) = @_;
$self->to_enumerable->order_by_descending(@_);
}
sub then_by_descending {
_then_by(@_[0..1], -1);
}
sub then_by {
_then_by(@_[0..1], 1);
}
sub _then_by {
my ($self, $key_extractor, $sign) = @_;
return _create $key_extractor, $sign, $self, sub {
my $top = $self;
my @ext = $key_extractor;
my @sgn = $sign;
for (my $c = $self; $c->isa(__PACKAGE__); $c = $c->{_par}) {
$top = $c;
unshift @ext, $c->{_key};
unshift @sgn, $c->{_sgn};
}
my @list = $top->to_perl;
# This is not written with efficiency in mind.
my @ordered = sort {
my $cmp = 0;
for (my $ix = 0; $ix < @ext; ++$ix) {
my $ext = $ext[$ix];
my $k1 = do { local $_ = $a; $ext->($_) };
my $k2 = do { local $_ = $b; $ext->($_) };
$cmp = $sgn[$ix] * ($k1 <=> $k2);
last if $cmp;
};
return $cmp;
} @list;
return Acme::IEnumerable->from_list(@ordered)->new;
};
}
1;
#####################################################################
#
#####################################################################
package Acme::IEnumerable::Grouping;
use strict;
use warnings;
use v5.10;
use Carp;
use base qw/Acme::IEnumerable::List/;
sub from_list {
my $class = shift;
my $key = shift;
my $self = Acme::IEnumerable->from_list(@_);
$self->{key} = $key;
bless $self, __PACKAGE__;
}
sub key { $_[0]->{key} }
1;
#####################################################################
#
#####################################################################
package Acme::IEnumerable;
use strict;
use warnings;
use v5.10;
use Carp;
do {
no warnings 'once';
*from_list = \&Acme::IEnumerable::List::from_list;
*to_array = \&Acme::IEnumerable::to_perl;
*order_by = \&Acme::IEnumerable::Ordered::then_by;
*order_by_descending =
\&Acme::IEnumerable::Ordered::then_by_descending;
};
sub _create {
bless {
_new => $_[0],
}, __PACKAGE__;
}
sub new { $_[0]->{_new}->() }
sub range {
my ($class, $from, $count) = @_;
if (defined $count) {
# ...
}
return _create sub {
return sub {
state $counter = $from // 0;
return \($counter++);
};
};
}
sub take {
my ($self, $count) = @_;
return _create sub {
return sub {
state $left = $count;
return unless $left;
$left--;
state $base = $self->new();
my $item = $base->();
return unless ref $item;
return $item;
};
};
}
sub take_until {
my ($self, $predicate) = @_;
return $self->take_while(sub {
!$predicate->($_);
});
}
sub take_while {
my ($self, $predicate) = @_;
return _create sub {
return sub {
state $base = $self->new();
my $item = $base->();
return unless ref $item;
local $_ = $$item;
return unless $predicate->($_);
return $item;
};
};
}
sub group_by {
my ($self, $key_extractor) = @_;
return _create sub {
( run in 1.608 second using v1.01-cache-2.11-cpan-6aa56a78535 )