Iterator::Util - Essential utilities for the Iterator class.


Iterator-Util documentation Contained in the Iterator-Util distribution.

Index


Code Index:

NAME

Top

Iterator::Util - Essential utilities for the Iterator class.

VERSION

Top

This documentation describes version 0.02 of Iterator::Util, August 23, 2005.

SYNOPSIS

Top

 use Iterator::Util;

 # Transform sequences
 $iterator = imap { transformation code } $some_other_iterator;

 # Filter sequences
 $iterator = igrep { condition code } $some_other_iterator;

 # Range of values  (arithmetic sequence)
 $iter = irange ($start, $end, $increment);
 $iter = irange ($start, $end);
 $iter = irange ($start);

 # Iterate over an arbitrary list
 $iter = ilist (item, item, ...);
 $iter = ilist (@items);

 # Iterate over an array, by reference
 $iter = iarray (\@array);

 # Return at most $num items from an iterator
 $iter   = ihead ($num, $some_other_iterator);
 @values = ihead ($num, $some_other_iterator);

 # Append multiple iterators into one
 $iter = iappend ($it1, $it2, $it3, ...);

 # Apply a function to pairs of iterator values
 $iter = ipairwise {code} $iter_A, $iter_B;

 # Skip the first $num values of an iterator
 $iter = iskip ($num, $some_other_iterator);

 # Skip values from an iterator until a condition is met
 $iter = iskip_until {code} $some_other_iterator;

 # Mesh iterators together
 $iter = imesh ($iter, $iter, ...);
 $iter = izip  ($iter, $iter, ...);

 # Return each value of an iterator once
 $iter = iuniq ($another_iterator);

DESCRIPTION

Top

This module implements many useful functions for creating and manipulating iterator objects.

An "iterator" is an object, represented as a code block that generates the "next value" of a sequence, and generally implemented as a closure. For further information, including a tutorial on using iterator objects, see the Iterator documentation.

FUNCTIONS

Top

imap
 $iter = imap { transformation code } $some_other_iterator;

Returns an iterator that is a transformation of some other iterator. Within the transformation code, $_ is set to each value of the other iterator, in turn.

Examples:

 $evens   = imap { $_ * 2  }  irange (0);  # returns 0, 2, 4, ...
 $squares = imap { $_ * $_ }  irange (7);  # 49, 64, 81, 100, ...

igrep
 $iter = igrep { condition } $some_other_iterator;

Returns an iterator that selectively returns values from some other iterator. Within the condition code, $_ is set to each value of the other iterator, in turn.

Examples:

 $fives = igrep { $_ % 5 == 0 } irange (0,10);   # returns 0, 5, 10
 $small = igrep { $_ < 10 }     irange (8,12);   # returns 8, 9

irange
 $iter = irange ($start, $end, $increment);
 $iter = irange ($start, $end);
 $iter = irange ($start);

irange returns a sequence of numbers. The sequence begins with $start, ends at $end, and steps by $increment. This is sort of the Iterator version of a for loop.

If $increment is not specified, 1 is used. $increment may be negative -- or even zero, in which case iterator returns an infinite sequence of $start.

If $end is not specified (is undef), the sequence is infinite.

Examples:

 $iter = irange (1, 2);           #  Iterate from 1 to 2
 $val  = $iter->value();          #  $val is now 1.
 $val  = $iter->value();          #  $val is now 2.
 $bool = $iter->is_exhausted();   #  $bool is now true.

 $iter = irange (10, 8, -1);      #  Iterate from 10 down to 8
 $iter = irange (1);              #  Iterate from 1, endlessly.

ilist
 $iter = ilist (@items);

Returns an iterator that iterates over an arbitrary sequence of values. It's sort of an Iterator version of foreach.

This function makes an internal copy of the list, so it may not be appropriate for an extremely large list.

Example:

 $iter = ilist (4, 'minus five', @foo, 7);
 $val  = $iter->value();          # $val is now 4
 $val  = $iter->value();          # $val is now 'minus five'
 ...

iarray
 $iter = iarray (\@array);

Returns an iterator that iterates over an array. Note that since it uses a reference to that array, if you modify the array, that will be reflected in the values returned by the iterator. This may be What You Want. Or it may cause Hard-To-Find Bugs.

ihead
 $iter   = ihead ($num, $some_other_iterator);
 @values = ihead ($num, $some_iterator);

In scalar context, creates an iterator that returns at most $num items from another iterator, then stops.

In list context, returns the first $num items from the iterator. If $num is undef, all remaining values are pulled from the iterator until it is exhausted. Use undef with caution; iterators can be huge -- or infinite.

Examples:

 $iota5 = ihead 5, irange 1;    # returns 1, 2, 3, 4, 5.

 $iter = irange 1;            # infinite sequence, starting with 1
 @vals = ihead (5, $iter);    # @vals is (1, 2, 3, 4, 5)
 $nextval = $iter->value;     # $nextval is 6.

iappend
 $iter = iappend (@list_of_iterators);

Creates an iterator that consists of any number of other iterators glued together. The resulting iterator pulls values from the first iterator until it's exhausted, then from the second, and so on.

ipairwise
 $iter = ipairwise {code} $it_A, $it_B;

Creates a new iterator which applies {code} to pairs of elements of two other iterators, $it_A and $it_B in turn. The pairs are assigned to $a and $b before invoking the code.

The new iterator is exhausted when either $it_A or $it_B are exhausted.

This function is analogous to the pairwise function from List::MoreUtils.

Example:

 $first  = irange 1;                              # 1,  2,  3,  4, ...
 $second = irange 4, undef, 2;                    # 4,  6,  8, 10, ...
 $third  = ipairwise {$a * $b} $first, $second;   # 4, 12, 24, 40, ...

iskip
 $iter = iskip ($num, $another_iterator);

Returns an iterator that contains the values of $another_iterator, minus the first $num values. In other words, skips the first $num values of $another_iterator.

Example:

 $iter = ilist (24, -1, 7, 8);        # Bunch of random values
 $cdr  = iskip 1, $iter;              # "pop" the first value
 $val  = $cdr->value();               # $val is -1.

iskip_until
 $iter = iskip_until {code} $another_iterator;

Returns an iterator that skips the leading values of $another_iterator until {code} evaluates to true for one of its values. {code} can refer to the current value as $_.

Example:

 $iter = iskip_until {$_ > 5}  irange 1;    # returns 6, 7, 8, 9, ...

imesh
izip
 $iter = imesh ($iter1, $iter2, ...);

This iterator accepts any number of other iterators, and "meshes" their values together. First it returns the first value of the first iterator, then the first value of the second iterator, and so on, until it has returned the first value of all of its iterator arguments. Then it goes back and returns the second value of the first iterator, and so on. It stops when any of its iterator arguments is exhausted.

Example:

 $i1 = ilist ('a', 'b', 'c');
 $i2 = ilist (1, 2, 3);
 $i3 = ilist ('rock', 'paper', 'scissors');
 $iter = imesh ($i1, $i2, $i3);
 # $iter will return, in turn, 'a', 1, 'rock', 'b', 2, 'paper', 'c',...

izip is a synonym for imesh.

iuniq
 $iter = iuniq ($another_iterator);

Creates an iterator to return unique values from another iterator; weeds out duplicates.

Example:

 $iter = ilist (1, 2, 2, 3, 1, 4);
 $uniq = iuniq ($iter);            # returns 1, 2, 3, 4.

EXPORTS

Top

All function names are exported to the caller's namespace by default.

DIAGNOSTICS

Top

Iterator::Util uses Exception::Class objects for throwing exceptions. If you're not familiar with Exception::Class, don't worry; these exception objects work just like $@ does with die and croak, but they are easier to work with if you are trapping errors.

See the Iterator module documentation for more information on trapping and handling these exceptions.

* Parameter Errors

Class: Iterator::X::Parameter_Error

You called an Iterator method with one or more bad parameters. Since this is almost certainly a coding error, there is probably not much use in handling this sort of exception.

As a string, this exception provides a human-readable message about what the problem was.

* Exhausted Iterators

Class: Iterator::X::Exhausted

You called value|Iterator/value on an iterator that is exhausted; that is, there are no more values in the sequence to return.

As a string, this exception is "Iterator is exhausted."

* User Code Exceptions

Class: Iterator::X::User_Code_Error

This exception is thrown when the sequence generation code throws any sort of error besides Am_Now_Exhausted. This could be because your code explicitly threw an error (that is, died), or because it otherwise encountered an exception (any runtime error).

This exception has one method, eval_error, which returns the original $@ that was trapped by the Iterator object. This may be a string or an object, depending on how die was invoked.

As a string, this exception evaluates to the stringified $@.

* I/O Errors

Class: Iterator::X::IO_Error

This exception is thrown when any sort of I/O error occurs; this only happens with the filesystem iterators.

This exception has one method, os_error, which returns the original $! that was trapped by the Iterator object.

As a string, this exception provides some human-readable information along with $!.

* Internal Errors

Class: Iterator::X::Internal_Error

Something happened that I thought couldn't possibly happen. I would appreciate it if you could send me an email message detailing the circumstances of the error.

REQUIREMENTS

Top

Requires the following additional modules:

Iterator

SEE ALSO

Top

Higher Order Perl, Mark Jason Dominus, Morgan Kauffman 2005.

http://perl.plover.com/hop/

THANKS

Top

Much thanks to Will Coleda and Paul Lalli (and the RPI lily crowd in general) for suggestions for the pre-release version.

AUTHOR / COPYRIGHT

Top


Iterator-Util documentation Contained in the Iterator-Util distribution.
use strict;
use warnings;
package Iterator::Util;
our $VERSION = '0.02';

use base 'Exporter';
use vars qw/@EXPORT @EXPORT_OK %EXPORT_TAGS/;

@EXPORT  = qw(imap igrep irange ilist iarray ihead iappend
              ipairwise iskip iskip_until imesh izip iuniq);

@EXPORT_OK   = (@EXPORT);

use Iterator;

# Function name: imap
# Synopsis:      $iter = imap {code} $another_iterator;
# Description:   Transforms an iterator.
# Created:       07/27/2005 by EJR
# Parameters:    code - Transformation code
#                $another_iterator - any other iterator.
# Returns:       Transformed iterator.
# Exceptions:    Iterator::X::Parameter_Error
#                Iterator::X::Am_Now_Exhausted
sub imap (&$)
{
    my ($transformation, $iter) = @_;

    Iterator::X::Parameter_Error->throw(q{Argument to imap must be an Iterator object})
        unless UNIVERSAL::isa($iter, 'Iterator');

    return Iterator->new( sub
    {
        Iterator::is_done if ($iter->is_exhausted);

        local $_ = $iter->value ();
        return $transformation-> ();
    });
}


# Function name: igrep
# Synopsis:      $iter = igrep {code} $another_iterator;
# Description:   Filters an iterator.
# Created:       07/27/2005 by EJR
# Parameters:    code - Filter condition.
#                $another_iterator - any other iterator.
# Returns:       Filtered iterator.
# Exceptions:    Iterator::X::Parameter_Error
#                Iterator::X::Am_Now_Exhausted
sub igrep (&$)
{
    my ($test, $iter) = @_;

    Iterator::X::Parameter_Error->throw(q{Argument to imap must be an Iterator object})
        unless UNIVERSAL::isa($iter, 'Iterator');

    return Iterator->new(sub
    {
        while ($iter->isnt_exhausted ())
        {
            local $_ = $iter->value ();
            return $_ if $test-> ();
        }

        Iterator::is_done();
    });
}


# Function name: irange
# Synopsis:      $iter = irange ($start, $end, $step);
# Description:   Generates an arithmetic sequence of numbers.
# Created:       07/27/2005 by EJR
# Parameters:    $start - First value.
#                $end   - Final value.     (may be omitted)
#                $step  - Increment value. (may be omitted)
# Returns:       Sequence iterator
# Exceptions:    Iterator::X::Am_Now_Exhausted
# Notes:         If the $end value is omitted, iterator is unbounded.
#                If $step is omitted, it defaults to 1.
#                $step may be negative (or even zero).
sub irange
{
    my ($from, $to, $step) = @_;
    $step = 1 unless defined $step;

    return Iterator->new (sub
    {
        # Reached limit?
        Iterator::is_done
                if (defined($to)
                    &&  ($step>0 && $from>$to  ||  $step<0 && $from<$to) );

        # This iteration's return value
        my $retval = $from;

        $from += $step;
        return $retval;
    });
}

# Function name: ilist
# Synopsis:      $iter = ilist (@list);
# Description:   Creates an iterator from a list
# Created:       07/28/2005 by EJR
# Parameters:    @list - list of values to iterate over
# Returns:       Array (list) iterator
# Exceptions:    Iterator::X::Am_Now_Exhausted
# Notes:         Makes an internal copy of the list.
sub ilist
{
    my @items = @_;
    my $index=0;
    return Iterator->new( sub
    {
        Iterator::is_done if ($index >= @items);
        return $items[$index++];
    });
}

# Function name: iarray
# Synopsis:      $iter = iarray ($a_ref);
# Description:   Creates an iterator from an array reference
# Created:       07/28/2005 by EJR
# Parameters:    $a_ref - Reference to array to iterate over
# Returns:       Array iterator
# Exceptions:    Iterator::X::Parameter_Error
#                Iterator::X::Am_Now_Exhausted
# Notes:         Does not make an internal copy of the list.
sub iarray ($)
{
    my $items = shift;
    my $index=0;

    Iterator::X::Parameter_Error->throw->
        (q{Argument to iarray must be an array reference})
            if ref $items ne 'ARRAY';

    return Iterator->new( sub
    {
        Iterator::is_done if $index >= @$items;
        return $items->[$index++];
    });
}

# Function name: ihead
# Synopsis:      $iter = ihead ($num, $some_other_iterator);
# Synopsis:      @valuse = ihead ($num, $iterator);
# Description:   Returns at most $num items from other iterator.
# Created:       07/28/2005 by EJR
#                08/02/2005 EJR: combined with ahead, per Will Coleda
# Parameters:    $num - Max number of items to return
#                $some_other_iterator - another iterator
# Returns:       limited iterator
# Exceptions:    Iterator::X::Parameter_Error
#                Iterator::X::Am_Now_Exhausted
sub ihead
{
    my $num  = shift;
    my $iter = shift;

    Iterator::X::Parameter_Error->throw
        (q{Second parameter for ihead must be an Iterator})
            unless UNIVERSAL::isa($iter, 'Iterator');

    # List context?  Return the first $num elements.
    if (wantarray)
    {
        my @a;
        while ($iter->isnt_exhausted  &&  (!defined($num)  ||  $num-- > 0))
        {
            push @a, $iter->value;
        }
        return @a;
    }

    # Scalar context: return an iterator to return at most $num elements.
    return Iterator->new(sub
    {
        Iterator::is_done if $num <= 0;

        $num--;
        return $iter->value;
    });
}

# Function name: iappend
# Synopsis:      $iter = iappend (@iterators);
# Description:   Joins a bunch of iterators together.
# Created:       07/28/2005 by EJR
# Parameters:    @iterators - any number of other iterators
# Returns:       A "merged" iterator.
# Exceptions:    Iterator::X::Parameter_Error
#                Iterator::X::Am_Now_Exhausted
sub iappend
{
    my @its = @_;

    # Check types
    foreach (@its)
    {
        Iterator::X::Parameter_Error->throw
            (q{All parameters for iarray must be Iterators})
                unless UNIVERSAL::isa($_, 'Iterator');
    }

    # Passthru, if there's only one.
    return $its[0] if @its == 1;

    return Iterator->new (sub
    {
        my $val;

        # Any empty iterators at front of list?  Remove'em.
        while (@its  &&  $its[0]->is_exhausted)
        {
            shift @its;
        }

        # No more iterators?  Then we're done.
        Iterator::is_done
            if @its == 0;

        # Return the next value of the iterator at the head of the list.
        return $its[0]->value;
    });
}

# Function name: ipairwise
# Synopsis:      $iter = ipairwise {code} ($iter1, $iter2);
# Description:   Applies an operation to pairs of values from iterators.
# Created:       07/28/2005 by EJR
# Parameters:    code - transformation, may use $a and $b
#                $iter1 - First iterator; "$a" value.
#                $iter2 - First iterator; "$b" value.
# Returns:       Iterator
# Exceptions:    Iterator::X::Parameter_Error
#                Iterator::X::Am_Now_Exhausted
sub ipairwise (&$$)
{
    my $op    = shift;
    my $iterA = shift;
    my $iterB = shift;

    # Check types
    for ($iterA, $iterB)
    {
        Iterator::X::Parameter_Error->throw
            (q{Second and third parameters for ipairwise must be Iterators})
                unless UNIVERSAL::isa($_, 'Iterator');
    }

    return Iterator->new(sub
    {
        Iterator::is_done
            if $iterA->is_exhausted  ||  $iterB->is_exhausted;

        # Localize $a and $b
        # My thanks to Benjamin Goldberg for this little bit of evil.
        my ($caller_a, $caller_b) = do
        {
            my $pkg;
            my $i = 1;
            while (1)
            {
                $pkg = caller($i++);
                last if $pkg ne 'Iterator'  &&  $pkg ne 'Iterator::Util';
            }
            no strict 'refs';
            \*{$pkg.'::a'}, \*{$pkg.'::b'};
        };

        # Set caller's $a and $b
        local (*$caller_a, *$caller_b) = \($iterA->value, $iterB->value);

        # Invoke caller's operation
        return $op->();
    });
}

# Function name: iskip
# Synopsis:      $iter = iskip $num, $another_iterator
# Description:   Skips the first $num values of another iterator
# Created:       07/28/2005 by EJR
# Parameters:    $num - how many values to skip
#                $another_iterator - another iterator
# Returns:       Sequence iterator
# Exceptions:    None
sub iskip
{
    my $num = shift;
    my $it  = shift;

    Iterator::X::Parameter_Error->throw
        (q{Second parameter for iskip must be an Iterator})
            unless UNIVERSAL::isa($it, 'Iterator');

    # Discard first $num values
    $it->value  while $it->isnt_exhausted  &&  $num-->0;

    return $it;
}


# Function name: iskip_until
# Synopsis:      $iter = iskip_until {code}, $another_iterator
# Description:   Skips values of another iterator until {code} is true.
# Created:       07/28/2005 by EJR
# Parameters:    {code} - Determines when to start returning values
#                $another_iterator - another iterator
# Returns:       Sequence iterator
# Exceptions:    Iterator::X::Am_Now_Exhausted
sub iskip_until (&$)
{
    my $code = shift;
    my $iter = shift;
    my $value;
    my $found_it = 0;

    Iterator::X::Parameter_Error->throw
        (q{Second parameter for iskip_until must be an Iterator})
            unless UNIVERSAL::isa($iter, 'Iterator');

    # Discard first $num values
    while ($iter->isnt_exhausted)
    {
        local $_ = $iter->value;
        if ($code->())
        {
            $found_it = 1;
            $value = $_;
            last;
        }
    }

    # Didn't find it?  Pity.
    Iterator::is_done
        unless $found_it;

    # Return an iterator with this value, and all remaining values.
    return iappend ilist($value), $iter;
}


# Function name: imesh / izip
# Synopsis:      $iter = imesh ($iter1, $iter2, ...)
# Description:   Merges other iterators together.
# Created:       07/30/2005 by EJR
# Parameters:    Any number of other iterators.
# Returns:       Sequence iterator
# Exceptions:    Iterator::X::Parameter_Error
#                Iterator::X::Am_Now_Exhausted
foreach my $sub (qw/imesh izip/)
{
    no strict 'refs';
    *$sub = sub
    {
        use strict 'refs';

        my @iterators = @_;
        my $it_index  = 0;

        foreach my $iter (@iterators)
        {
            Iterator::X::Parameter_Error->throw(
                "Argument to $sub is not an iterator")
                unless UNIVERSAL::isa($iter, 'Iterator');
        }

        return Iterator->new (sub
        {
            Iterator::is_done
                if $iterators[$it_index]->is_exhausted();

            my $retval = $iterators[$it_index]->value();

            if (++$it_index >= @iterators)
            {
                $it_index = 0;
            }

            return $retval;
        });
    };
}

# Function name: iuniq
# Synopsis:      $iter = iuniq ($another_iterator);
# Description:   Removes duplicate entries from an iterator.
# Created:       07/30/2005 by EJR
# Parameters:    Another iterator.
# Returns:       Sequence iterator
# Exceptions:    Iterator::X::Parameter_Error
#                Iterator::X::Am_Now_Exhausted
sub iuniq
{
    Iterator::X::Parameter_Error->throw ("Too few parameters to iuniq")
        if @_ < 1;
    Iterator::X::Parameter_Error->throw ("Too many parameters to iuniq")
        if @_ > 1;

    my $iter = shift;
    Iterator::X::Parameter_Error->throw("Argument to iuniq is not an iterator")
        unless UNIVERSAL::isa($iter, 'Iterator');

    my %did_see;
    return Iterator->new (sub
    {
        my $value;
        while (1)
        {
            Iterator::is_done
                if $iter->is_exhausted;

            $value = $iter->value;
            last if !$did_see{$value}++;
        }
        return $value;
    });
}

1;
__END__