[code-review] Algorithm::GenerateSequence

Richard Clamp <richardc-PThgL6HeIXdH4x6Dk/[email protected]> Sat, 6 Sep 2003 05:51:18 +0100
Newsgroups gmane.comp.lang.perl.code-review-ladder
Message-ID <[email protected]>
I needed this to write Games::Set (possibly a forthcoming post) and
couldn't for the life of me find anything that looked like it by
searching on such terms as Counter, Iterator, Generator, Sequence.

Now what wheel is it I've reinvented, and how round is this one?

Thanks.

-- 
Richard Clamp <richardc-PThgL6HeIXdH4x6Dk/[email protected]>
GenerateSequence.pm (application/x-perl, 1.9 KB)
use strict;
package Algorithm::GenerateSequence;
our $VERSION = '1.21';
use base 'Class::Accessor::Fast';
__PACKAGE__->mk_accessors(qw( _values _counters _started _ended ));

=head1 NAME

Algorithm::GenerateSequence - a sequence generator

=head1 SYNOPSIS

 my $gen = Algorithm::GenerateSequence->new(
    [qw( one two three )], [qw( hey bee )],
 );
 print $gen->next, "\n"; # onehey
 print $gen->next, "\n"; # onebee
 print $gen->next, "\n"; # twohey
 print $gen->next, "\n"; # twobee
 ...

=head1 DESCRIPTION

=head1 METHODS

=head2 new( @values );

values contains arrays of values which will be treated in sequence

=cut

sub new {
    my $class = shift;
    $class->SUPER::new({
        _values   => [ @_ ],
        _counters => [ map { 0 } @_ ],
    });
}

=head1 next

returns a list containing the next value in the sequence, false after
the end of the list.

=cut

sub next {
    my $self = shift;

    return if $self->_ended;

    if ($self->_started) {
        my $max = @{ $self->_counters } - 1;

        # mmm, long addition
        do {
            my $new = ++$self->_counters->[ $max ];
            # check for overflow
            goto DONE if $new % @{ $self->_values->[ $max ] };
            $self->_counters->[ $max ] = 0;
        } while --$max >= 0;
      DONE:
        if ($max < 0) {
            $self->_ended(1);
            return;
        }
    }
    $self->_started(1);

    my $i = 0;
    return map {
        $self->_values->[ $i++ ][ $_ ]
    } @{ $self->_counters };
}

=head2 reset

reset the iterator

=cut

sub reset {
    my $self = shift;
    $_ = 0 for @{ $self->_counters };
    $self->_started(0);
    $self->_ended(0);
}

=head2 as_list

return the entire set as a list of arrays

=cut

sub as_list {
    my $self = shift;
    $self->reset;
    my @results;

    while (my @next = $self->next) {
        push @results, \@next;
    }

    $self->reset;
    return @results;
}

1;
__END__
ags.t (application/x-troff, 1 KB) - not displayed