NAME

Iterator::Simple - Simple iterator and utilities

SYNOPSIS

use Iterator::Simple;

sub foo {
  my $max = shift;
  my $i = 0;
  iterator {
    return if $i > $max;
    $i++;
  }
}

my $iterator = foo(20); # yields 0,1,2, ..., 19, 20;
$iterator = imap { $_ + 2 } $iterator; # yields 2,3,4,5, ... ,20,21,22
$iterator = igrep { $_ % 2 } $iterator; # yields 3,5,7,9, ... ,17,19,21

# iterable object
$iterator = iter([qw(foo bar baz)]); # iterator from array ref
$iterator = iter(IO::File->new($filename)); # iterator from GLOB

# filters
$iterator = ichain($itr1, $itr2); # chain iterators;
$iterator = izip($itr1, $itr2); # zip iterators;
$iterator = ienumerate $iterator; # add index;

# general filter
$iterator = ifilter $iterator, sub {
  return $_ if /^A/;
  return;
}

# how to iterate
while(defined($_ = $iterator->())) {
  print;
}

while(defined($_ = $iterator->next)) {
  print;
}

while(<iterator>) {
  print;
}

DESCRIPTION

Iterator::Simple is yet another general-purpose iterator utilities.

Rather simple, but powerful and fast iterator.

FUNCTIONS

Iterator::Simple doesn't export any functions by default. please import them like:

use Iterator::Simple qw(iter list imap);

For all functions:

use Iterator::Simple qw(:all);
iterator { CODE }

Iterator constructor. CODE returns a value on each call, and if it is exhausted, returns undef. Therefore, you cannot yields undefined value as a meaning value. If you want, you could use Iterator module which can do that.

Generally, you can implement iterator as a closure like:

use Iterator::Simple qw(iterator);

sub fibonacci {
  my($s1, $s2, $max) = @_;
  
  iterator {
    my $rv;
    ($rv, $s1, $s2) = ($s1, $s2, $s1 + $s2);
    return if $rv > $max;
    return $rv;
  }
}

my $iterator = fiboacci(1, 1, 1000);

You can iterate it in several ways:

  • just call it

    while(defined($_ = $iterator->())) {
      print "$_\n";
    }
  • next method

    while(defined($_ = $iterator->next)) {
      print "$_\n";
    }
  • <> operator

    while(<$iterator>) {
      print "$_\n";
    }
is_iterator($object)

If $object is an iterator created by Iterator::Simple, returns true. False otherwise.

iter($object)

This function auto detects what $object is, and automatically turns it into an iterator. Supported objects are:

  • Iterator made with Iterator::Simple.

  • Object that implements __iter__ method.

  • Object that overloads '<>' or has next method.

  • Object that overloads '&{}'.(as iterator function.)

  • Object that overloads '@{}'.(with iarray())

  • ARRAY reference. (iarray())

  • CODE reference. (as iterator function.)

  • GLOB reference.

  • nothing (iter().) (empty iterator.)

If it fails to convert, runtime error.

is_iterable($object)

return true if $object can be converted with iter($object)

list($object)

This function converts $object into single array referece.

  • ARRAY reference.

  • GLOB reference.

  • Iterator made with Iterator::Simple.

  • Object that overloads '@{}' operator.

  • Object that implements '__iter__' method.

  • Object that overloads '<>' operator or has next method.

  • nothing (i.e. list() returns []);

If it fails to convert, runtime error.

Note that after list($iterator), that iterator is not usable any more.

imap { CODE } $iterable

This is the iterator version of map. Returns an iterator which yields the value from source iterator modified by CODE.

igrep { CODE } $iterable

This is the iterator version of grep. Returns an iterator which yields the value from source iterator only when CODE returns true value.

iflatten $iterable

When $iterable yields another iterator, iterate it first.

$subitr = iter([10, 11,12]);
$source = iter([ 1, 2, $subitr, 4]);

$flattened = iflatten $source;

# yields 1, 2, 10, 11, 12, 4.
ifilter $iterable, sub{ CODE }

This is the combination of imap, igrep, iflatten. it supports modify (imap) , skip (igrep), and inflate (iflatten). but it should be faster than combination of them.

For example:

$combination = iflatten
  imap { $_ eq 'baz' ? iter(['whoa', 'who']) : ":$_:" }
  igrep { $_ ne 'bar' }
  iter [ 'foo', 'bar', 'baz', 'fiz' ];

$itr = iter [ 'foo', 'bar', 'baz', 'fiz' ];
$filterd = ifilter $itr, sub {
  return if $_ eq 'bar'; #skip
  return iter(['whoa', 'who']) if $_ eq 'baz'; #inflate
  return ":$_:"; # modify
};

Both of them will yields ':foo:', 'whoa', 'who', ':fiz:'.

ichain($iterable, $iterable2, ...)

This function returns an iterator which chains one or more iterators. Iterates each iterables in order as is, until each iterables are exhausted.

Example:

$itr1 = iter(['foo', 'bar', 'baz']);
$itr2 = iter(['hoge', 'hage']);

$chained = ichain($itr1, $itr2);

# yields 'foo', 'bar', 'baz', 'hoge', 'hage'.
ienumerate($iterable)

This function returns an iterator yields like:

$ary = iter(['foo', 'bar', 'baz', ... ]);

$iter = ienumerate $ary;

# yields [0, 'foo'], [1, 'bar'], [2, 'baz'], ... 
izip($iterable, $iterable2, ...);

Accepts one or more iterables, returns an iterator like:

$animals = iter(['dogs', 'cats', 'pigs']);
$says = iter(['bowwow', 'mew', 'oink']);

$zipped = izip($animals, $says);

# yields ['dogs','bowwow'], ['cats','mew'], ['pigs', 'oink'].

Note that when one of source iterables is exhausted, zipped iterator will be exhausted also.

islice($iterable, $start, $end, $step)

Same as islice of itertools in Python. If $end is undef or negative value, it iterates source until it is exhausted. $step defaults to 1. 0 or negative step value is prohibited.

$iter = iter([0,1,2,3,4,5,6,7,8,9,10,11,12]);

$sliced = islice($iter, 3, 13, 2);

# yields 3, 5, 7, 9, 11.
ihead($count, $iterable)
islice($iterable, 0, $count, 1);
iskip($count, $iterable)
islice($iterable, $count, undef, 1);
iarray($arrayref);

Turns array reference into an iterator. Used in iter($arrayref). You do not have to use this function directly, because iter($arrayref) is sufficient.

OO INTERFACE

Iterator used in Iterator::Simple is just a code reference blessed in Iterator::Simple::Iterator. This class implements several method and overloads some operators.

Itrator::Simple::Iterator->new($coderef)

Just bless $coderef in Iterator::Simple::Iterator and returns it.

$iterator->next

Call underlying code.

$iterator->__iter__

Returns self. You don't need to use this.

Overloaded operators.
  • Read filehandle operator '<>'

    Overloading '<>' makes this possible like:

    print while <$iterator>;
  • Pipe.. bit_OR? .. No, pipe!

    $iterator | $coderef1 | $coderef2;

    is equivalent to:

    $iterator->filter($coderef1)->filter($coderef2);

    is equivalent to:

    ifilter(ifilter($iterator, $coderef1), $coderef2);
$iterator->filter($coderef)
$iterator->flatten()
$iterator->chain($another, ..)
$iterator->zip($another, ..)
$iterator->enumerate()
$iterator->slice($start, $end, $step)
$iterator->head($count)
$iterator->skip($count)

For example, $iterator->flatten() is equivalent to iflatten $iterator.

TIPS

All iterator transformation function calls iter function on all source iterables. So you can pass just array reference, GLOB ref, etc.

These examples completely do the right thing:

imap { $_ + 2 } [1, 2, 3, ... ];
ienumerate(\*STDIN);

# DBIx::Class::ResultSet has 'next' method.
ifilter $dbic_resultset, sub {CODE};

You can implement __iter__ method on your objects in your application. By doing that, your object will be Iterator::Simple friendly :).

Note that __iter__ method must return an iterator.

Why Not Iterator.pm

There is another iterator module in CPAN, named Iterator and Iterator::Util made by Eric J. Roode that is great solution. Why yet another iterator module? The answer is *Speed*. You use iterator because you have too many data to manipulate in memory, therefore iterator could be called thousands of times, speed is important.

For this simple example:

use Iterator::Util qw(iarray imap igrep);

for(1 .. 100) {
  my $itr = igrep { $_ % 2 } imap { $_ + 2 } iarray([1 .. 1000]);
  my @result;
  while($itr->isnt_exhausted) {
    push @result, $itr->value;
  }
}

meanwhile:

use Iterator::Simple qw(iarray imap igrep);

for(1 .. 100) {
  my $itr = igrep { $_ % 2 } imap { $_ + 2 } iarray([1 .. 1000]);
  my @result;
  while(defined($_ = $itr->())) {
    push @result, $_;
  }
}

Iterator::Simple is about ten times faster!

That is natural because Iterator::Simple iterator is just a code reference, while Iterator.pm iterator is full featured class instance. But Iterator::Simple is sufficient for usual demands.

One of most downside of Iterator::Simple is, you cannot yields undef value as a meaning value, because Iterator::Simple thinks it as a exhausted sign. If you need to do that, you have to yield something which represents undef value.

Also, Iterator::Simple cannot determine iterator is exhausted until next iteration, while Iterator.pm has 'is(nt)_exhausted' method which is useful in some situation.

AUTHOR

Rintaro Ishizaki <rintaro@cpan.org>

LICENSE

This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.

SEE ALSO