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 )