Import Upstream version 0.02
This commit is contained in:
commit
9ce9388092
|
@ -0,0 +1,7 @@
|
|||
Revision history for Perl extension Iterator.
|
||||
|
||||
0.01 2005 August 18
|
||||
- First version
|
||||
|
||||
0.02 2005 August 23
|
||||
- Fixed several documentation typos.
|
Binary file not shown.
|
@ -0,0 +1,13 @@
|
|||
<SOFTPKG NAME="Iterator-Util" VERSION="0,01,0,0">
|
||||
<TITLE>Iterator-Util</TITLE>
|
||||
<ABSTRACT>Essential utilities for the Iterator class.</ABSTRACT>
|
||||
<AUTHOR>Eric Roode <roode@cpan.org></AUTHOR>
|
||||
<IMPLEMENTATION>
|
||||
<DEPENDENCY NAME="Exception-Class" VERSION="1,21,0,0" />
|
||||
<DEPENDENCY NAME="Iterator" VERSION="0,01,0,0" />
|
||||
<DEPENDENCY NAME="Test-Simple" VERSION="0,40,0,0" />
|
||||
|
||||
|
||||
<CODEBASE HREF="" />
|
||||
</IMPLEMENTATION>
|
||||
</SOFTPKG>
|
|
@ -0,0 +1,17 @@
|
|||
Changes
|
||||
Makefile.PL
|
||||
MANIFEST
|
||||
README
|
||||
Util.pm
|
||||
Iterator-Util.ppd
|
||||
Iterator-Util-ppm.tar.gz
|
||||
t/doc.t
|
||||
t/iappend.t
|
||||
t/iarray.t
|
||||
t/ihead.t
|
||||
t/ilist.t
|
||||
t/imap.t
|
||||
t/ipairwise.t
|
||||
t/irange.t
|
||||
t/iskip.t
|
||||
META.yml Module meta-data (added by MakeMaker)
|
|
@ -0,0 +1,13 @@
|
|||
# http://module-build.sourceforge.net/META-spec.html
|
||||
#XXXXXXX This is a prototype!!! It will change in the future!!! XXXXX#
|
||||
name: Iterator-Util
|
||||
version: 0.02
|
||||
version_from: Util.pm
|
||||
installdirs: site
|
||||
requires:
|
||||
Exception::Class: 1.21
|
||||
Iterator: 0.01
|
||||
Test::Simple: 0.40
|
||||
|
||||
distribution_type: module
|
||||
generated_by: ExtUtils::MakeMaker version 6.17
|
|
@ -0,0 +1,64 @@
|
|||
use ExtUtils::MakeMaker;
|
||||
# See lib/ExtUtils/MakeMaker.pm for details of how to influence
|
||||
# the contents of the Makefile that is written.
|
||||
WriteMakefile(
|
||||
'NAME' => 'Iterator::Util',
|
||||
'VERSION_FROM' => 'Util.pm', # finds $VERSION
|
||||
'PREREQ_PM' => {'Test::Simple' => '0.40',
|
||||
'Exception::Class' => '1.21',
|
||||
'Iterator' => '0.01',
|
||||
},
|
||||
($] >= 5.005 ? ## Add these new keywords supported since 5.005
|
||||
(ABSTRACT_FROM => 'Util.pm', # retrieve abstract from module
|
||||
AUTHOR => 'Eric Roode <roode@cpan.org>') : ()),
|
||||
);
|
||||
|
||||
|
||||
package MY;
|
||||
|
||||
sub dist_core
|
||||
{
|
||||
my $text = shift->SUPER::dist_core(@_);
|
||||
$text =~ s/^(\$\(DISTVNAME\)[^:]*): (.*)/$1: ppd ppm $2/mg;
|
||||
return $text;
|
||||
}
|
||||
|
||||
sub realclean
|
||||
{
|
||||
my $text = shift->SUPER::realclean(@_);
|
||||
$text .= <<'CLEAN';
|
||||
rm -rf $(PPDFILE) $(PPMFILE)
|
||||
CLEAN
|
||||
return $text;
|
||||
}
|
||||
|
||||
sub ppd
|
||||
{
|
||||
my $self = shift;
|
||||
my $text = $self->SUPER::ppd(@_);
|
||||
$text =~ s/(ppd\s*:)/$1 \$(PPDFILE)\n\n\$(PPDFILE) :/;
|
||||
$text =~ s[(?<=<CODEBASE HREF=\\")]
|
||||
[http://search.cpan.org/src/ROODE/$self->{DISTNAME}-$self->{VERSION}/$self->{DISTNAME}-ppm.tar.gz];
|
||||
|
||||
# This release is allegedly OS and architecture independent (as it's pure perl)
|
||||
$text =~ s/<OS NAME[^>]+>(?:\\[nt])*//;
|
||||
$text =~ s/<ARCHITECTURE NAME[^>]+>(?:\\[nt])*//;
|
||||
|
||||
$text = <<'PRE' . $text;
|
||||
|
||||
PPMNAME = $(DISTNAME)-ppm
|
||||
PPDFILE = $(DISTNAME).ppd
|
||||
PPMFILE = $(PPMNAME).tar.gz
|
||||
|
||||
PRE
|
||||
|
||||
$text .= <<'PPM';
|
||||
|
||||
|
||||
ppm: $(PPMFILE)
|
||||
|
||||
$(PPMFILE): pm_to_blib $(INST_LIBDIR)/.exists $(INST_ARCHAUTODIR)/.exists $(INST_AUTODIR)/.exists
|
||||
$(TAR) $(TARFLAGS) - blib | $(COMPRESS) -c > $(PPMFILE)
|
||||
PPM
|
||||
return $text;
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
Iterator::Util version 0.02
|
||||
===========================
|
||||
|
||||
This module contains many useful utility functions for use with the
|
||||
Iterator class (q.v.). The inspiration for many of these functions
|
||||
has been Mark Jason Dominus's lectures and recent book (_Higher Order
|
||||
Perl_, Morgan Kauffman, 2005).
|
||||
|
||||
An "iterator" is an object, represented as a code block that generates
|
||||
the "next value" of a sequence, and generally implemented as a
|
||||
closure. Iterator.pm provides a class that simplifies creation and
|
||||
use of these iterator objects, and provides many general-purpose
|
||||
iterator functions.
|
||||
|
||||
EXAMPLES
|
||||
|
||||
$it = irange (1, 3);
|
||||
|
||||
$i = $it->value; # returns 1
|
||||
$i = $it->value; # returns 2
|
||||
$i = $it->value; # returns 3
|
||||
$i = $it->value; # throws an Iterator::X::Exhausted exception.
|
||||
|
||||
$another_it = imap {$_ * $_} irange (7, 10);
|
||||
|
||||
while ($another_it->isnt_exhausted)
|
||||
{
|
||||
print $another_it->value, "\n";
|
||||
}
|
||||
# The above prints 49, 64, 81, 100 and throws no exceptions.
|
||||
# Another call to $another_it->value would throw an exception.
|
||||
|
||||
DEVELOPMENT STATE
|
||||
|
||||
This is a brand-new module. It has a decent test suite, but has
|
||||
not been extensively field-tested. Therefore, it should be considered
|
||||
"beta" software, and used with care.
|
||||
|
||||
If you find any bugs, or if any behavior of Iterator surprises you,
|
||||
I would be grateful if you could send me an email message about it.
|
||||
Thanks.
|
||||
|
||||
|
||||
INSTALLATION
|
||||
|
||||
To install this module, do the standard Perl module four-step:
|
||||
|
||||
perl Makefile.PL or perl Makefile.pl LIB='my/install/path'
|
||||
make
|
||||
make test
|
||||
make install
|
||||
|
||||
DEPENDENCIES
|
||||
|
||||
This module requires these other modules and libraries:
|
||||
|
||||
Iterator
|
||||
|
||||
COPYRIGHT AND LICENSE
|
||||
|
||||
Eric J. Roode, roode@cpan.org
|
||||
|
||||
To avoid my spam filter, please include "Perl", "module", or this
|
||||
module's name in the message's subject line, and/or GPG-sign your
|
||||
message.
|
||||
|
||||
Copyright (c) 2005 by Eric J. Roode. All Rights Reserved.
|
||||
This module is free software; you can redistribute it and/or modify it
|
||||
under the same terms as Perl itself.
|
|
@ -0,0 +1,806 @@
|
|||
=for gpg
|
||||
-----BEGIN PGP SIGNED MESSAGE-----
|
||||
Hash: SHA1
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Iterator::Util - Essential utilities for the Iterator class.
|
||||
|
||||
=head1 VERSION
|
||||
|
||||
This documentation describes version 0.02 of Iterator::Util, August 23, 2005.
|
||||
|
||||
=cut
|
||||
|
||||
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__
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
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);
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
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 L<Iterator> documentation.
|
||||
|
||||
=head1 FUNCTIONS
|
||||
|
||||
=over 4
|
||||
|
||||
=item imap
|
||||
|
||||
$iter = imap { transformation code } $some_other_iterator;
|
||||
|
||||
Returns an iterator that is a transformation of some other iterator.
|
||||
Within the transformation code, C<$_> is set to each value of the
|
||||
other iterator, in turn.
|
||||
|
||||
I<Examples:>
|
||||
|
||||
$evens = imap { $_ * 2 } irange (0); # returns 0, 2, 4, ...
|
||||
$squares = imap { $_ * $_ } irange (7); # 49, 64, 81, 100, ...
|
||||
|
||||
=item igrep
|
||||
|
||||
$iter = igrep { condition } $some_other_iterator;
|
||||
|
||||
Returns an iterator that selectively returns values from some other
|
||||
iterator. Within the C<condition> code, C<$_> is set to each value of
|
||||
the other iterator, in turn.
|
||||
|
||||
I<Examples:>
|
||||
|
||||
$fives = igrep { $_ % 5 == 0 } irange (0,10); # returns 0, 5, 10
|
||||
$small = igrep { $_ < 10 } irange (8,12); # returns 8, 9
|
||||
|
||||
=item irange
|
||||
|
||||
$iter = irange ($start, $end, $increment);
|
||||
$iter = irange ($start, $end);
|
||||
$iter = irange ($start);
|
||||
|
||||
C<irange> returns a sequence of numbers. The sequence begins with
|
||||
C<$start>, ends at C<$end>, and steps by C<$increment>. This is sort
|
||||
of the Iterator version of a C<for> loop.
|
||||
|
||||
If C<$increment> is not specified, 1 is used. C<$increment> may be
|
||||
negative -- or even zero, in which case iterator returns an infinite
|
||||
sequence of C<$start>.
|
||||
|
||||
If C<$end> is not specified (is C<undef>), the sequence is infinite.
|
||||
|
||||
I<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.
|
||||
|
||||
=item ilist
|
||||
|
||||
$iter = ilist (@items);
|
||||
|
||||
Returns an iterator that iterates over an arbitrary sequence of
|
||||
values. It's sort of an Iterator version of C<foreach>.
|
||||
|
||||
This function makes an internal copy of the list, so it may not be
|
||||
appropriate for an extremely large list.
|
||||
|
||||
I<Example:>
|
||||
|
||||
$iter = ilist (4, 'minus five', @foo, 7);
|
||||
$val = $iter->value(); # $val is now 4
|
||||
$val = $iter->value(); # $val is now 'minus five'
|
||||
...
|
||||
|
||||
=item 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.
|
||||
|
||||
=item ihead
|
||||
|
||||
$iter = ihead ($num, $some_other_iterator);
|
||||
@values = ihead ($num, $some_iterator);
|
||||
|
||||
In scalar context, creates an iterator that returns at most C<$num>
|
||||
items from another iterator, then stops.
|
||||
|
||||
In list context, returns the first C<$num> items from the iterator.
|
||||
If C<$num> is C<undef>, all remaining values are pulled
|
||||
from the iterator until it is exhausted. Use C<undef> with caution;
|
||||
iterators can be huge -- or infinite.
|
||||
|
||||
I<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.
|
||||
|
||||
=item 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.
|
||||
|
||||
=item ipairwise
|
||||
|
||||
$iter = ipairwise {code} $it_A, $it_B;
|
||||
|
||||
Creates a new iterator which applies C<{code}> to pairs of elements of
|
||||
two other iterators, C<$it_A> and C<$it_B> in turn. The pairs are
|
||||
assigned to C<$a> and C<$b> before invoking the code.
|
||||
|
||||
The new iterator is exhausted when either C<$it_A> or C<$it_B> are
|
||||
exhausted.
|
||||
|
||||
This function is analogous to the L<pairwise|List::MoreUtils/pairwise>
|
||||
function from L<List::MoreUtils>.
|
||||
|
||||
I<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, ...
|
||||
|
||||
=item iskip
|
||||
|
||||
$iter = iskip ($num, $another_iterator);
|
||||
|
||||
Returns an iterator that contains the values of C<$another_iterator>,
|
||||
minus the first C<$num> values. In other words, skips the first
|
||||
C<$num> values of C<$another_iterator>.
|
||||
|
||||
I<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.
|
||||
|
||||
=item iskip_until
|
||||
|
||||
$iter = iskip_until {code} $another_iterator;
|
||||
|
||||
Returns an iterator that skips the leading values of C<$another_iterator>
|
||||
until C<{code}> evaluates to true for one of its values. C<{code}>
|
||||
can refer to the current value as C<$_>.
|
||||
|
||||
I<Example:>
|
||||
|
||||
$iter = iskip_until {$_ > 5} irange 1; # returns 6, 7, 8, 9, ...
|
||||
|
||||
=item imesh
|
||||
|
||||
=item 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.
|
||||
|
||||
I<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',...
|
||||
|
||||
C<izip> is a synonym for C<imesh>.
|
||||
|
||||
=item iuniq
|
||||
|
||||
$iter = iuniq ($another_iterator);
|
||||
|
||||
Creates an iterator to return unique values from another iterator;
|
||||
weeds out duplicates.
|
||||
|
||||
I<Example:>
|
||||
|
||||
$iter = ilist (1, 2, 2, 3, 1, 4);
|
||||
$uniq = iuniq ($iter); # returns 1, 2, 3, 4.
|
||||
|
||||
=back
|
||||
|
||||
=head1 EXPORTS
|
||||
|
||||
All function names are exported to the caller's namespace by default.
|
||||
|
||||
=head1 DIAGNOSTICS
|
||||
|
||||
Iterator::Util uses L<Exception::Class> objects for throwing
|
||||
exceptions. If you're not familiar with Exception::Class, don't
|
||||
worry; these exception objects work just like C<$@> does with C<die>
|
||||
and C<croak>, but they are easier to work with if you are trapping
|
||||
errors.
|
||||
|
||||
See the L<Iterator|Iterator/DIAGNOSTICS> module documentation for more
|
||||
information on trapping and handling these exceptions.
|
||||
|
||||
=over 4
|
||||
|
||||
=item * Parameter Errors
|
||||
|
||||
Class: C<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.
|
||||
|
||||
=item * Exhausted Iterators
|
||||
|
||||
Class: C<Iterator::X::Exhausted>
|
||||
|
||||
You called C<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."
|
||||
|
||||
=item * User Code Exceptions
|
||||
|
||||
Class: C<Iterator::X::User_Code_Error>
|
||||
|
||||
This exception is thrown when the sequence generation code throws any
|
||||
sort of error besides C<Am_Now_Exhausted>. This could be because your
|
||||
code explicitly threw an error (that is, C<die>d), or because it
|
||||
otherwise encountered an exception (any runtime error).
|
||||
|
||||
This exception has one method, C<eval_error>, which returns the
|
||||
original C<$@> that was trapped by the Iterator object. This may be a
|
||||
string or an object, depending on how C<die> was invoked.
|
||||
|
||||
As a string, this exception evaluates to the stringified C<$@>.
|
||||
|
||||
=item * I/O Errors
|
||||
|
||||
Class: C<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, C<os_error>, which returns the original
|
||||
C<$!> that was trapped by the Iterator object.
|
||||
|
||||
As a string, this exception provides some human-readable information
|
||||
along with C<$!>.
|
||||
|
||||
=item * Internal Errors
|
||||
|
||||
Class: C<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.
|
||||
|
||||
=back
|
||||
|
||||
=head1 REQUIREMENTS
|
||||
|
||||
Requires the following additional modules:
|
||||
|
||||
L<Iterator>
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
I<Higher Order Perl>, Mark Jason Dominus, Morgan Kauffman 2005.
|
||||
|
||||
L<http://perl.plover.com/hop/>
|
||||
|
||||
=head1 THANKS
|
||||
|
||||
Much thanks to Will Coleda and Paul Lalli (and the RPI lily crowd in
|
||||
general) for suggestions for the pre-release version.
|
||||
|
||||
=head1 AUTHOR / COPYRIGHT
|
||||
|
||||
Eric J. Roode, roode@cpan.org
|
||||
|
||||
Copyright (c) 2005 by Eric J. Roode. All Rights Reserved.
|
||||
This module is free software; you can redistribute it and/or modify it
|
||||
under the same terms as Perl itself.
|
||||
|
||||
To avoid my spam filter, please include "Perl", "module", or this
|
||||
module's name in the message's subject line, and/or GPG-sign your
|
||||
message.
|
||||
|
||||
=cut
|
||||
|
||||
=begin gpg
|
||||
|
||||
-----BEGIN PGP SIGNATURE-----
|
||||
Version: GnuPG v1.4.1 (Cygwin)
|
||||
|
||||
iD8DBQFDC5UFY96i4h5M0egRApNiAJ9WwoZql+2DE+RsSA6koGLZPcbQZACfY248
|
||||
VoKah+WAFOvk46vOcn+hL9Y=
|
||||
=aOws
|
||||
-----END PGP SIGNATURE-----
|
||||
|
||||
=end gpg
|
|
@ -0,0 +1,220 @@
|
|||
use strict;
|
||||
use Test::More tests => 33;
|
||||
use Iterator::Util;
|
||||
|
||||
# Check that the documentation examples work.
|
||||
|
||||
my @vals;
|
||||
|
||||
## From the POD
|
||||
|
||||
# $evens (3);
|
||||
{
|
||||
my $evens;
|
||||
|
||||
@vals = ();
|
||||
eval
|
||||
{
|
||||
$evens = imap { $_ * 2 } irange (0); # returns 0, 2, 4, ...
|
||||
};
|
||||
is ($@, q{}, q{$evens created fine});
|
||||
eval
|
||||
{
|
||||
push @vals, $evens->value for (1..3);
|
||||
};
|
||||
is ($@, q{}, q{$evens executed fine});
|
||||
is_deeply(\@vals, [0, 2, 4], q{$evens returns what I said it would});
|
||||
}
|
||||
|
||||
# $squares (3)
|
||||
{
|
||||
my $squares;
|
||||
|
||||
@vals = ();
|
||||
eval
|
||||
{
|
||||
$squares = imap { $_ * $_ } irange (7); # 49, 64, 81, 100, ...
|
||||
};
|
||||
is ($@, q{}, q{$squares created fine});
|
||||
eval
|
||||
{
|
||||
push @vals, $squares->value for (1..4);
|
||||
};
|
||||
is ($@, q{}, q{$squares created fine});
|
||||
is_deeply(\@vals, [49, 64, 81, 100], q{$squares returns what I said it would});
|
||||
}
|
||||
|
||||
# $fives (3)
|
||||
{
|
||||
my $fives;
|
||||
|
||||
@vals = ();
|
||||
eval
|
||||
{
|
||||
$fives = igrep { $_ % 5 == 0 } irange (0,10); # returns 0, 5, 10
|
||||
};
|
||||
is ($@, q{}, q{$fives created fine});
|
||||
eval
|
||||
{
|
||||
push @vals, $fives->value while $fives->isnt_exhausted;
|
||||
};
|
||||
is ($@, q{}, q{$fives created fine});
|
||||
is_deeply(\@vals, [0, 5, 10], q{$fives returns what I said it would});
|
||||
}
|
||||
|
||||
# $small (3)
|
||||
{
|
||||
my $small;
|
||||
|
||||
@vals = ();
|
||||
eval
|
||||
{
|
||||
$small = igrep { $_ < 10 } irange (8,12); # returns 8, 9
|
||||
};
|
||||
is ($@, q{}, q{$small created fine});
|
||||
eval
|
||||
{
|
||||
push @vals, $small->value while $small->isnt_exhausted;
|
||||
};
|
||||
is ($@, q{}, q{$small executed fine});
|
||||
is_deeply(\@vals, [8, 9], q{$small returns what I said it would});
|
||||
}
|
||||
|
||||
# $iota5 (3)
|
||||
{
|
||||
my $iota5;
|
||||
|
||||
@vals = ();
|
||||
eval
|
||||
{
|
||||
$iota5 = ihead 5, irange 1; # returns 1, 2, 3, 4, 5.
|
||||
};
|
||||
is ($@, q{}, q{$iota5 created fine});
|
||||
eval
|
||||
{
|
||||
push @vals, $iota5->value while $iota5->isnt_exhausted;
|
||||
};
|
||||
is ($@, q{}, q{$iota5 executed fine});
|
||||
is_deeply(\@vals, [1, 2, 3, 4, 5], q{$iota5 returns what I said it would});
|
||||
}
|
||||
|
||||
# ipairwise (3)
|
||||
{
|
||||
my ($first, $second, $third);
|
||||
|
||||
@vals = ();
|
||||
eval
|
||||
{
|
||||
no warnings 'once';
|
||||
$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, ...
|
||||
};
|
||||
is ($@, q{}, q{1, 2, 3 iterators created fine});
|
||||
eval
|
||||
{
|
||||
push @vals, $third->value for (1..4);
|
||||
};
|
||||
is ($@, q{}, q{$third executed fine});
|
||||
is_deeply(\@vals, [4, 12, 24, 40], q{$ithird returns what I said it would});
|
||||
}
|
||||
|
||||
# $cdr (3)
|
||||
{
|
||||
my ($cdr);
|
||||
|
||||
@vals = ();
|
||||
eval
|
||||
{
|
||||
my $iter = ilist (24, -1, 7, 8); # Bunch of random values
|
||||
$cdr = iskip 1, $iter; # "pop" the first value
|
||||
};
|
||||
is ($@, q{}, q{$cdr iterators created fine});
|
||||
eval
|
||||
{
|
||||
push @vals, $cdr->value while $cdr->isnt_exhausted;
|
||||
};
|
||||
is ($@, q{}, q{$cdr executed fine});
|
||||
is_deeply(\@vals, [-1, 7, 8], q{$cdr returns what I said it would});
|
||||
}
|
||||
|
||||
# skip_until (3)
|
||||
{
|
||||
my $iter;
|
||||
|
||||
@vals = ();
|
||||
eval
|
||||
{
|
||||
$iter = iskip_until {$_ > 5} irange 1; # returns 6, 7, 8, 9, ...
|
||||
};
|
||||
is ($@, q{}, q{$iter iterators created fine});
|
||||
eval
|
||||
{
|
||||
push @vals, $iter->value for (1..4);
|
||||
};
|
||||
is ($@, q{}, q{$iter executed fine});
|
||||
is_deeply(\@vals, [6, 7, 8, 9], q{$iter returns what I said it would});
|
||||
}
|
||||
|
||||
# imesh (3)
|
||||
{
|
||||
my $iter;
|
||||
|
||||
@vals = ();
|
||||
eval
|
||||
{
|
||||
my $i1 = ilist ('a', 'b', 'c');
|
||||
my $i2 = ilist (1, 2, 3);
|
||||
my $i3 = ilist ('rock', 'paper', 'scissors');
|
||||
$iter = imesh ($i1, $i2, $i3);
|
||||
};
|
||||
is ($@, q{}, q{imesh iterator created fine});
|
||||
eval
|
||||
{
|
||||
# $iter will return, in turn, 'a', 1, 'rock', 'b', 2, 'paper', 'c',...
|
||||
push @vals, $iter->value for (1..7);
|
||||
};
|
||||
is ($@, q{}, q{imesh executed fine});
|
||||
is_deeply(\@vals, ['a', 1, 'rock', 'b', 2, 'paper', 'c'], q{imesh returns what I said it would});
|
||||
}
|
||||
|
||||
# izip (3)
|
||||
{
|
||||
my $iter;
|
||||
|
||||
@vals = ();
|
||||
eval
|
||||
{
|
||||
my $i1 = ilist ('a', 'b', 'c');
|
||||
my $i2 = ilist (1, 2, 3);
|
||||
my $i3 = ilist ('rock', 'paper', 'scissors');
|
||||
$iter = izip ($i1, $i2, $i3);
|
||||
};
|
||||
is ($@, q{}, q{izip iterator created fine});
|
||||
eval
|
||||
{
|
||||
# $iter will return, in turn, 'a', 1, 'rock', 'b', 2, 'paper', 'c',...
|
||||
push @vals, $iter->value for (1..7);
|
||||
};
|
||||
is ($@, q{}, q{izip executed fine});
|
||||
is_deeply(\@vals, ['a', 1, 'rock', 'b', 2, 'paper', 'c'], q{izip returns what I said it would});
|
||||
}
|
||||
|
||||
# iuniq (3)
|
||||
{
|
||||
my $uniq;
|
||||
|
||||
@vals = ();
|
||||
eval
|
||||
{
|
||||
my $iter = ilist (1, 2, 2, 3, 1, 4);
|
||||
$uniq = iuniq ($iter); # returns 1, 2, 3, 4.
|
||||
};
|
||||
is ($@, q{}, q{iuniq iterator created fine});
|
||||
eval
|
||||
{
|
||||
push @vals, $uniq->value while $uniq->isnt_exhausted;
|
||||
};
|
||||
is ($@, q{}, q{iuniq executed fine});
|
||||
is_deeply(\@vals, [1, 2, 3, 4], q{iuniq returns what I said it would});
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
use strict;
|
||||
use Test::More tests => 2;
|
||||
use Iterator::Util;
|
||||
|
||||
# Check that iappend works as promised.
|
||||
|
||||
sub begins_with
|
||||
{
|
||||
my ($actual, $expected, $test_name) = @_;
|
||||
|
||||
$actual = substr($actual, 0, length $expected);
|
||||
@_ = ($actual, $expected, $test_name);
|
||||
goto &is;
|
||||
}
|
||||
|
||||
my ($iter, @vals);
|
||||
|
||||
# iappend (2)
|
||||
eval
|
||||
{
|
||||
my $it1 = irange 1,3;
|
||||
my $it2 = irange 20,30,8;
|
||||
my $it3 = irange 110, 100, -5;
|
||||
$iter = iappend $it1, $it2, $it3;
|
||||
|
||||
push @vals, $iter->value while $iter->isnt_exhausted;
|
||||
};
|
||||
|
||||
is $@, q{}, q{Created iappend iterator, no errors};
|
||||
is_deeply (\@vals, [1, 2, 3, 20, 28, 110, 105, 100], q{iappend returned expected values});
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
use strict;
|
||||
use Test::More tests => 6;
|
||||
use Iterator::Util;
|
||||
|
||||
# Check that iarray works as promised.
|
||||
|
||||
sub begins_with
|
||||
{
|
||||
my ($actual, $expected, $test_name) = @_;
|
||||
|
||||
$actual = substr($actual, 0, length $expected);
|
||||
@_ = ($actual, $expected, $test_name);
|
||||
goto &is;
|
||||
}
|
||||
|
||||
my ($iter, $x, @vals, @reported);
|
||||
|
||||
@vals = (1, 2, 'foo', [qw/a b c/]);
|
||||
|
||||
# basic iarray. (3)
|
||||
eval
|
||||
{
|
||||
$iter = iarray \@vals;
|
||||
};
|
||||
|
||||
is $@, q{}, q{Created iarray iterator, no errors};
|
||||
@reported = ();
|
||||
|
||||
eval
|
||||
{
|
||||
push @reported, $iter->value while $iter->isnt_exhausted;
|
||||
};
|
||||
is $@, q{}, q{Executed array iterator, no errors};
|
||||
is_deeply (\@reported, [1, 2, 'foo', [qw/a b c/]], q{iarray returned expected values});
|
||||
|
||||
|
||||
# changing iarray. (3)
|
||||
eval
|
||||
{
|
||||
$iter = iarray \@vals;
|
||||
};
|
||||
|
||||
is $@, q{}, q{Created iarray iterator, no errors};
|
||||
@reported = ();
|
||||
|
||||
eval
|
||||
{
|
||||
push @reported, $iter->value for (1..3);
|
||||
# Change the underlying array:
|
||||
push @vals, 'Mark Jason Dominus is God';
|
||||
$vals[0] = '666';
|
||||
push @reported, $iter->value while $iter->isnt_exhausted;
|
||||
};
|
||||
is $@, q{}, q{Executed array iterator, no errors};
|
||||
is_deeply (\@reported, [1, 2, 'foo', [qw/a b c/], q{Mark Jason Dominus is God}],
|
||||
q{iarray returned expected values});
|
||||
|
|
@ -0,0 +1,144 @@
|
|||
use strict;
|
||||
use Test::More tests => 29;
|
||||
use Iterator::Util;
|
||||
|
||||
# Check that ihead works as promised.
|
||||
|
||||
sub begins_with
|
||||
{
|
||||
my ($actual, $expected, $test_name) = @_;
|
||||
|
||||
$actual = substr($actual, 0, length $expected);
|
||||
@_ = ($actual, $expected, $test_name);
|
||||
goto &is;
|
||||
}
|
||||
|
||||
my ($iter, $x, $next, @vals);
|
||||
|
||||
# ihead (3)
|
||||
eval
|
||||
{
|
||||
$iter = ihead 5, imap { $_ * $_ } irange 4;
|
||||
};
|
||||
|
||||
$x = $@;
|
||||
is $x, q{}, q{Created ihead iterator, no errors};
|
||||
|
||||
@vals = ();
|
||||
eval
|
||||
{
|
||||
push @vals, $iter->value while $iter->isnt_exhausted;
|
||||
};
|
||||
is $x, q{}, q{Executed ihead iterator, no errors};
|
||||
is_deeply (\@vals, [16, 25, 36, 49, 64], q{ihead returned expected values});
|
||||
|
||||
|
||||
# ihead, zero (3)
|
||||
eval
|
||||
{
|
||||
$iter = ihead 0, imap { $_ * $_ } irange 4;
|
||||
};
|
||||
|
||||
$x = $@;
|
||||
is $x, q{}, q{Created ihead iterator, no errors};
|
||||
|
||||
@vals = ();
|
||||
eval
|
||||
{
|
||||
push @vals, $iter->value while $iter->isnt_exhausted;
|
||||
};
|
||||
is $x, q{}, q{Executed ihead iterator, no errors};
|
||||
is_deeply (\@vals, [], q{ihead returned expected values});
|
||||
|
||||
|
||||
# ihead, negative (3)
|
||||
eval
|
||||
{
|
||||
$iter = ihead -77, imap { $_ * $_ } irange 4;
|
||||
};
|
||||
|
||||
$x = $@;
|
||||
is $x, q{}, q{Created ihead iterator, no errors};
|
||||
|
||||
@vals = ();
|
||||
eval
|
||||
{
|
||||
push @vals, $iter->value while $iter->isnt_exhausted;
|
||||
};
|
||||
is $x, q{}, q{Executed ihead iterator, no errors};
|
||||
is_deeply (\@vals, [], q{ihead returned expected values});
|
||||
|
||||
|
||||
# ihead, parameter error (4)
|
||||
eval
|
||||
{
|
||||
$iter = ihead -77, 4;
|
||||
};
|
||||
|
||||
$x = $@;
|
||||
isnt $x, q{}, q{Created ihead iterator, no errors};
|
||||
ok (Iterator::X->caught(), q{ihead exception: proper base class});
|
||||
ok (Iterator::X::Parameter_Error->caught(), q{ihead exception: proper specific class});
|
||||
begins_with ($x, q{Second parameter for ihead must be an Iterator},
|
||||
q{ihead exception formatted properly.});
|
||||
|
||||
|
||||
# ihead (3)
|
||||
eval
|
||||
{
|
||||
$iter = imap { $_ * $_ } irange 4;
|
||||
@vals = ihead 5, $iter;
|
||||
$next = $iter->value;
|
||||
};
|
||||
|
||||
is $@, q{}, q{Called ihead, no errors};
|
||||
is_deeply (\@vals, [16, 25, 36, 49, 64], q{ihead returned expected values});
|
||||
cmp_ok ($next, '==', 81, q{Iterator advanced correctly});
|
||||
|
||||
# ihead, zero (3)
|
||||
eval
|
||||
{
|
||||
$iter = imap { $_ * $_ } irange 4;
|
||||
@vals = ihead 0, $iter;
|
||||
$next = $iter->value;
|
||||
};
|
||||
|
||||
is $@, q{}, q{Created ihead iterator, no errors};
|
||||
is_deeply (\@vals, [], q{ihead returned expected values});
|
||||
cmp_ok ($next, '==', 16, q{Iterator advanced correctly});
|
||||
|
||||
# ihead, negative (3)
|
||||
eval
|
||||
{
|
||||
$iter = imap { $_ * $_ } irange 4;
|
||||
@vals = ihead -64, $iter;
|
||||
$next = $iter->value;
|
||||
};
|
||||
|
||||
is $@, q{}, q{Created ihead iterator, no errors};
|
||||
is_deeply (\@vals, [], q{ihead returned expected values});
|
||||
cmp_ok ($next, '==', 16, q{Iterator advanced correctly});
|
||||
|
||||
# ihead, undef (3)
|
||||
eval
|
||||
{
|
||||
$iter = imap { $_ * $_ } irange 4, 6;
|
||||
@vals = ihead undef, $iter;
|
||||
};
|
||||
|
||||
is $@, q{}, q{Created ihead iterator, no errors};
|
||||
is_deeply (\@vals, [16, 25, 36], q{ihead returned expected values});
|
||||
ok ($iter->is_exhausted, q{ihead exhausted the iterator});
|
||||
|
||||
# ihead, parameter error (4)
|
||||
eval
|
||||
{
|
||||
$iter = ihead -77, 4;
|
||||
};
|
||||
|
||||
$x = $@;
|
||||
isnt $x, q{}, q{Created ihead iterator, no errors};
|
||||
ok (Iterator::X->caught(), q{ihead exception: proper base class});
|
||||
ok (Iterator::X::Parameter_Error->caught(), q{ihead exception: proper specific class});
|
||||
begins_with ($x, q{Second parameter for ihead must be an Iterator},
|
||||
q{ihead exception formatted properly.});
|
|
@ -0,0 +1,36 @@
|
|||
use strict;
|
||||
use Test::More tests => 3;
|
||||
use Iterator::Util;
|
||||
|
||||
# Check that ilist works as promised.
|
||||
|
||||
sub begins_with
|
||||
{
|
||||
my ($actual, $expected, $test_name) = @_;
|
||||
|
||||
$actual = substr($actual, 0, length $expected);
|
||||
@_ = ($actual, $expected, $test_name);
|
||||
goto &is;
|
||||
}
|
||||
|
||||
my ($iter, $x, @vals);
|
||||
|
||||
@vals = (1, 2, 'foo', [qw/a b c/]);
|
||||
|
||||
# ilist (3)
|
||||
eval
|
||||
{
|
||||
$iter = ilist @vals;
|
||||
};
|
||||
|
||||
$x = $@;
|
||||
is $x, q{}, q{Created ilist iterator, no errors};
|
||||
@vals = ();
|
||||
|
||||
eval
|
||||
{
|
||||
push @vals, $iter->value while $iter->isnt_exhausted;
|
||||
};
|
||||
is $x, q{}, q{Executed ilist iterator, no errors};
|
||||
is_deeply (\@vals, [1, 2, 'foo', [qw/a b c/]], q{ilist returned expected values});
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
use strict;
|
||||
use Test::More tests => 13;
|
||||
use Iterator::Util;
|
||||
|
||||
# Check that imap function works (assumes irange works).
|
||||
|
||||
sub begins_with
|
||||
{
|
||||
my ($actual, $expected, $test_name) = @_;
|
||||
|
||||
$actual = substr($actual, 0, length $expected);
|
||||
@_ = ($actual, $expected, $test_name);
|
||||
goto &is;
|
||||
}
|
||||
|
||||
my ($iter, $x, $worked, @vals);
|
||||
|
||||
## Parameter error (4)
|
||||
eval
|
||||
{
|
||||
$iter = imap { $_ * 2} 'oops';
|
||||
};
|
||||
|
||||
$x = $@;
|
||||
isnt ($@, q{}, q{Wrong-type; exception thrown});
|
||||
ok (Iterator::X->caught(), q{Wrong-type base exception type});
|
||||
ok (Iterator::X::Parameter_Error->caught(), q{Wrong-type specific exception type});
|
||||
begins_with ($x, q{Argument to imap must be an Iterator object},
|
||||
q{Wrong-type exception formatted properly});
|
||||
|
||||
## run an irange through an imap. (1)
|
||||
eval
|
||||
{
|
||||
$iter = imap { $_ * 2 } irange (0);
|
||||
};
|
||||
|
||||
is ($@, q{}, q{Normal; no exception thrown});
|
||||
|
||||
# pull a few numbers out of the hat. (2)
|
||||
@vals = ();
|
||||
eval
|
||||
{
|
||||
push @vals, $iter->value() for (1..4);
|
||||
};
|
||||
|
||||
is ($@, q{}, q{No exception when imapping});
|
||||
is_deeply (\@vals, [0, 2, 4, 6], q{imap transformation returned expected result});
|
||||
|
||||
# Now do it with a finite irange (2)
|
||||
@vals = ();
|
||||
eval
|
||||
{
|
||||
$iter = imap {$_ * $_} irange (1, 4);
|
||||
push @vals, $iter->value while $iter->isnt_exhausted;
|
||||
};
|
||||
|
||||
is ($@, q{}, q{No exception when imapping.});
|
||||
is_deeply (\@vals, [1, 4, 9, 16], q{Square imap returned expected results});
|
||||
|
||||
# Try pushing it further: (4)
|
||||
eval
|
||||
{
|
||||
push @vals, $iter->value;
|
||||
};
|
||||
|
||||
$x = $@;
|
||||
isnt ($@, q{}, q{Imapped too far; exception thrown});
|
||||
ok (Iterator::X->caught(), q{Too-far base exception type});
|
||||
ok (Iterator::X::Exhausted->caught(), q{Too-far specific exception type});
|
||||
begins_with ($x, q{Iterator is exhausted},
|
||||
q{Too-far exception formatted properly});
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
use strict;
|
||||
use Test::More tests => 2;
|
||||
use Iterator::Util;
|
||||
|
||||
# Check that ipairwise works as promised.
|
||||
|
||||
|
||||
sub begins_with
|
||||
{
|
||||
my ($actual, $expected, $test_name) = @_;
|
||||
|
||||
$actual = substr($actual, 0, length $expected);
|
||||
@_ = ($actual, $expected, $test_name);
|
||||
goto &is;
|
||||
}
|
||||
|
||||
my ($iter, @vals);
|
||||
|
||||
# ipairwise (2)
|
||||
eval
|
||||
{
|
||||
no warnings 'once';
|
||||
|
||||
my $first = irange 1; # 1, 2, 3, 4, ...
|
||||
my $second = irange 4, undef, 2; # 4, 6, 8, 10, ...
|
||||
my $third = ipairwise {$a * $b} $first, $second; # 4, 12, 24, 40, ...
|
||||
|
||||
push @vals, $third->value for (1..4)
|
||||
};
|
||||
|
||||
is $@, q{}, q{Created ipairwise iterator, no errors};
|
||||
is_deeply (\@vals, [4, 12, 24, 40], q{ipairwise returned expected values});
|
||||
|
|
@ -0,0 +1,269 @@
|
|||
use strict;
|
||||
use Test::More tests => 42;
|
||||
use Iterator::Util;
|
||||
|
||||
# Check that irange function works.
|
||||
|
||||
sub begins_with
|
||||
{
|
||||
my ($actual, $expected, $test_name) = @_;
|
||||
|
||||
$actual = substr($actual, 0, length $expected);
|
||||
@_ = ($actual, $expected, $test_name);
|
||||
goto &is;
|
||||
}
|
||||
|
||||
my ($iter, $x, $worked, @vals);
|
||||
|
||||
## One-arg irange (infinite) (3)
|
||||
eval
|
||||
{
|
||||
$iter = irange(52);
|
||||
};
|
||||
|
||||
is $@, q{}, q{Created one-arg iterator; no exception};
|
||||
|
||||
# How do you test an infinite iterator?
|
||||
# Well, let's run through a bunch of iterations, see if it pans out.
|
||||
$worked = 1; # assume okay
|
||||
eval
|
||||
{
|
||||
foreach my $test (52..151) # try a hundred values
|
||||
{
|
||||
if ($test != $iter->value || $iter->is_exhausted)
|
||||
{
|
||||
$worked = 0;
|
||||
last;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
is ($@, q{}, q{Looped over one-arg iterator; no exception});
|
||||
ok ($worked, q{One-arg iterator gave expected values});
|
||||
|
||||
|
||||
## Two-arg irange (start, end). (3)
|
||||
eval
|
||||
{
|
||||
$iter = irange (4, 6);
|
||||
};
|
||||
|
||||
is ($@, q{}, q{Created two-arg iterator; no exception});
|
||||
|
||||
@vals = ();
|
||||
eval
|
||||
{
|
||||
push @vals, $iter->value while $iter->isnt_exhausted;
|
||||
};
|
||||
|
||||
is ($@, q{}, q{Invoked two-arg iterator until exhausted});
|
||||
is_deeply (\@vals, [4, 5, 6], q{Two-arg iterator returned expected results});
|
||||
|
||||
|
||||
## Two-arg irange (start, end), end < start. (3)
|
||||
eval
|
||||
{
|
||||
$iter = irange (6, 4);
|
||||
};
|
||||
|
||||
is ($@, q{}, q{Created two-arg iterator; no exception});
|
||||
|
||||
@vals = ();
|
||||
eval
|
||||
{
|
||||
push @vals, $iter->value while $iter->isnt_exhausted;
|
||||
};
|
||||
|
||||
is ($@, q{}, q{Invoked two-arg iterator until exhausted});
|
||||
is_deeply (\@vals, [], q{Two-arg iterator returned expected results});
|
||||
|
||||
|
||||
## Two-arg irange (start, end), end == start. (3)
|
||||
eval
|
||||
{
|
||||
$iter = irange (6, 6);
|
||||
};
|
||||
|
||||
is ($@, q{}, q{Created two-arg iterator; no exception});
|
||||
|
||||
@vals = ();
|
||||
eval
|
||||
{
|
||||
push @vals, $iter->value while $iter->isnt_exhausted;
|
||||
};
|
||||
|
||||
is ($@, q{}, q{Invoked two-arg iterator until exhausted});
|
||||
is_deeply (\@vals, [6], q{Two-arg iterator returned expected results});
|
||||
|
||||
|
||||
## Three-arg irange (start, end, step). (3)
|
||||
eval
|
||||
{
|
||||
$iter = irange (28, 41, 7);
|
||||
};
|
||||
|
||||
is ($@, q{}, q{Created three-arg iterator; no exception});
|
||||
|
||||
@vals = ();
|
||||
eval
|
||||
{
|
||||
push @vals, $iter->value while $iter->isnt_exhausted;
|
||||
};
|
||||
|
||||
is ($@, q{}, q{Invoked three-arg iterator until exhausted});
|
||||
is_deeply (\@vals, [28, 35], q{Three-arg iterator returned expected results});
|
||||
|
||||
## Three-arg irange (start, end, step). (3)
|
||||
eval
|
||||
{
|
||||
$iter = irange (28, 42, 7);
|
||||
};
|
||||
|
||||
is ($@, q{}, q{Created three-arg iterator; no exception});
|
||||
|
||||
@vals = ();
|
||||
eval
|
||||
{
|
||||
push @vals, $iter->value while $iter->isnt_exhausted;
|
||||
};
|
||||
|
||||
is ($@, q{}, q{Invoked three-arg iterator until exhausted});
|
||||
is_deeply (\@vals, [28, 35, 42], q{Three-arg iterator returned expected results});
|
||||
|
||||
## Three-arg irange (start, end, step). (3)
|
||||
eval
|
||||
{
|
||||
$iter = irange (28, 43, 7);
|
||||
};
|
||||
|
||||
is ($@, q{}, q{Created three-arg iterator; no exception});
|
||||
|
||||
@vals = ();
|
||||
eval
|
||||
{
|
||||
push @vals, $iter->value while $iter->isnt_exhausted;
|
||||
};
|
||||
|
||||
is ($@, q{}, q{Invoked three-arg iterator until exhausted});
|
||||
is_deeply (\@vals, [28, 35, 42], q{Three-arg iterator returned expected results});
|
||||
|
||||
## Three-arg irange (start, end, negative step). (3)
|
||||
eval
|
||||
{
|
||||
$iter = irange (42, 29, -7);
|
||||
};
|
||||
|
||||
is ($@, q{}, q{Created three-arg iterator; no exception});
|
||||
|
||||
@vals = ();
|
||||
eval
|
||||
{
|
||||
push @vals, $iter->value while $iter->isnt_exhausted;
|
||||
};
|
||||
|
||||
is ($@, q{}, q{Invoked three-arg iterator until exhausted});
|
||||
is_deeply (\@vals, [42, 35], q{Three-arg iterator returned expected results});
|
||||
|
||||
## Three-arg irange (start, end, negative step). (3)
|
||||
eval
|
||||
{
|
||||
$iter = irange (42, 28, -7);
|
||||
};
|
||||
|
||||
is ($@, q{}, q{Created three-arg iterator; no exception});
|
||||
|
||||
@vals = ();
|
||||
eval
|
||||
{
|
||||
push @vals, $iter->value while $iter->isnt_exhausted;
|
||||
};
|
||||
|
||||
is ($@, q{}, q{Invoked three-arg iterator until exhausted});
|
||||
is_deeply (\@vals, [42, 35, 28], q{Three-arg iterator returned expected results});
|
||||
|
||||
## Three-arg irange (start, end, negative step). (3)
|
||||
eval
|
||||
{
|
||||
$iter = irange (42, 27, -7);
|
||||
};
|
||||
|
||||
is ($@, q{}, q{Created three-arg iterator; no exception});
|
||||
|
||||
@vals = ();
|
||||
eval
|
||||
{
|
||||
push @vals, $iter->value while $iter->isnt_exhausted;
|
||||
};
|
||||
|
||||
is ($@, q{}, q{Invoked three-arg iterator until exhausted});
|
||||
is_deeply (\@vals, [42, 35, 28], q{Three-arg iterator returned expected results});
|
||||
|
||||
|
||||
## Three-arg irange (start, end, zero step). (3)
|
||||
eval
|
||||
{
|
||||
$iter = irange (28, 42, 0);
|
||||
};
|
||||
|
||||
is ($@, q{}, q{Created three-arg iterator; no exception});
|
||||
|
||||
@vals = ();
|
||||
eval
|
||||
{
|
||||
push @vals, $iter->value for (0..9);
|
||||
};
|
||||
|
||||
is ($@, q{}, q{Invoked three-arg (zero) iterator for a while});
|
||||
is_deeply (\@vals, [28, 28, 28, 28, 28, 28, 28, 28, 28, 28], q{Three-arg iterator returned expected results});
|
||||
|
||||
## Three-arg irange (start, end, step) (1 iteration, give or take). (3)
|
||||
eval
|
||||
{
|
||||
$iter = irange (28, 27, 7);
|
||||
};
|
||||
|
||||
is ($@, q{}, q{Created three-arg iterator; no exception});
|
||||
|
||||
@vals = ();
|
||||
eval
|
||||
{
|
||||
push @vals, $iter->value while $iter->isnt_exhausted;
|
||||
};
|
||||
|
||||
is ($@, q{}, q{Invoked three-arg terator until exhausted});
|
||||
is_deeply (\@vals, [], q{Three-arg iterator returned expected results});
|
||||
|
||||
## Three-arg irange (start, end, step) (1 iteration, give or take). (3)
|
||||
eval
|
||||
{
|
||||
$iter = irange (28, 28, 7);
|
||||
};
|
||||
|
||||
is ($@, q{}, q{Created three-arg iterator; no exception});
|
||||
|
||||
@vals = ();
|
||||
eval
|
||||
{
|
||||
push @vals, $iter->value while $iter->isnt_exhausted;
|
||||
};
|
||||
|
||||
is ($@, q{}, q{Invoked three-arg terator until exhausted});
|
||||
is_deeply (\@vals, [28], q{Three-arg iterator returned expected results});
|
||||
|
||||
## Three-arg irange (start, end, step) (1 iteration, give or take). (3)
|
||||
eval
|
||||
{
|
||||
$iter = irange (28, 34, 7);
|
||||
};
|
||||
|
||||
is ($@, q{}, q{Created three-arg iterator; no exception});
|
||||
|
||||
@vals = ();
|
||||
eval
|
||||
{
|
||||
push @vals, $iter->value while $iter->isnt_exhausted;
|
||||
};
|
||||
|
||||
is ($@, q{}, q{Invoked three-arg terator until exhausted});
|
||||
is_deeply (\@vals, [28], q{Three-arg iterator returned expected results});
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
use strict;
|
||||
use Test::More tests => 4;
|
||||
use Iterator::Util;
|
||||
|
||||
# Check that iskip works as promised.
|
||||
|
||||
sub begins_with
|
||||
{
|
||||
my ($actual, $expected, $test_name) = @_;
|
||||
|
||||
$actual = substr($actual, 0, length $expected);
|
||||
@_ = ($actual, $expected, $test_name);
|
||||
goto &is;
|
||||
}
|
||||
|
||||
my ($iter, @vals);
|
||||
|
||||
# iskip (2)
|
||||
@vals = ();
|
||||
eval
|
||||
{
|
||||
$iter = iskip 5, irange 8;
|
||||
push @vals, $iter->value for (1..4);
|
||||
};
|
||||
|
||||
is $@, q{}, q{Created iskip iterator, no errors};
|
||||
is_deeply (\@vals, [13, 14, 15, 16], q{iskip returned expected values.});
|
||||
|
||||
|
||||
# iskip_until (2)
|
||||
@vals = ();
|
||||
eval
|
||||
{
|
||||
$iter = iskip_until {$_ % 5 == 0} irange 8;
|
||||
push @vals, $iter->value for (1..4);
|
||||
};
|
||||
|
||||
is $@, q{}, q{Created iskip_until iterator, no errors};
|
||||
is_deeply (\@vals, [10, 11, 12, 13], q{iskip_until returned expected values.});
|
||||
|
Loading…
Reference in New Issue