From 9ce93880926d1e94329ea0c5103d2b9e827c7b11 Mon Sep 17 00:00:00 2001 From: su-fang Date: Tue, 27 Sep 2022 15:12:00 +0800 Subject: [PATCH] Import Upstream version 0.02 --- Changes | 7 + Iterator-Util-ppm.tar.gz | Bin 0 -> 6966 bytes Iterator-Util.ppd | 13 + MANIFEST | 17 + META.yml | 13 + Makefile.PL | 64 ++++ README | 69 ++++ Util.pm | 806 +++++++++++++++++++++++++++++++++++++++ t/doc.t | 220 +++++++++++ t/iappend.t | 31 ++ t/iarray.t | 57 +++ t/ihead.t | 144 +++++++ t/ilist.t | 36 ++ t/imap.t | 72 ++++ t/ipairwise.t | 33 ++ t/irange.t | 269 +++++++++++++ t/iskip.t | 40 ++ 17 files changed, 1891 insertions(+) create mode 100644 Changes create mode 100644 Iterator-Util-ppm.tar.gz create mode 100644 Iterator-Util.ppd create mode 100644 MANIFEST create mode 100644 META.yml create mode 100644 Makefile.PL create mode 100644 README create mode 100644 Util.pm create mode 100644 t/doc.t create mode 100644 t/iappend.t create mode 100644 t/iarray.t create mode 100644 t/ihead.t create mode 100644 t/ilist.t create mode 100644 t/imap.t create mode 100644 t/ipairwise.t create mode 100644 t/irange.t create mode 100644 t/iskip.t diff --git a/Changes b/Changes new file mode 100644 index 0000000..d2d17a0 --- /dev/null +++ b/Changes @@ -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. diff --git a/Iterator-Util-ppm.tar.gz b/Iterator-Util-ppm.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..bdf3cad10493ae7df958b1ed6e59f8e70af18ded GIT binary patch literal 6966 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 IX8@o80F%RpIsgCw literal 0 HcmV?d00001 diff --git a/Iterator-Util.ppd b/Iterator-Util.ppd new file mode 100644 index 0000000..56f0d24 --- /dev/null +++ b/Iterator-Util.ppd @@ -0,0 +1,13 @@ + + Iterator-Util + Essential utilities for the Iterator class. + Eric Roode <roode@cpan.org> + + + + + + + + + diff --git a/MANIFEST b/MANIFEST new file mode 100644 index 0000000..e74350f --- /dev/null +++ b/MANIFEST @@ -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) diff --git a/META.yml b/META.yml new file mode 100644 index 0000000..be1dca2 --- /dev/null +++ b/META.yml @@ -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 diff --git a/Makefile.PL b/Makefile.PL new file mode 100644 index 0000000..076de7c --- /dev/null +++ b/Makefile.PL @@ -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 ') : ()), +); + + +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[(?<={DISTNAME}-$self->{VERSION}/$self->{DISTNAME}-ppm.tar.gz]; + + # This release is allegedly OS and architecture independent (as it's pure perl) + $text =~ s/]+>(?:\\[nt])*//; + $text =~ s/]+>(?:\\[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; +} diff --git a/README b/README new file mode 100644 index 0000000..c1a8f52 --- /dev/null +++ b/README @@ -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. diff --git a/Util.pm b/Util.pm new file mode 100644 index 0000000..4aed6c2 --- /dev/null +++ b/Util.pm @@ -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 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 + + $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 code, C<$_> is set to each value of +the other iterator, in turn. + +I + + $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 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 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), the sequence is infinite. + +I + + $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. + +This function makes an internal copy of the list, so it may not be +appropriate for an extremely large list. + +I + + $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, all remaining values are pulled +from the iterator until it is exhausted. Use C with caution; +iterators can be huge -- or infinite. + +I + + $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 +function from L. + +I + + $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 + + $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 + + $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 + + $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 is a synonym for C. + +=item iuniq + + $iter = iuniq ($another_iterator); + +Creates an iterator to return unique values from another iterator; +weeds out duplicates. + +I + + $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 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 +and C, but they are easier to work with if you are trapping +errors. + +See the L module documentation for more +information on trapping and handling these exceptions. + +=over 4 + +=item * Parameter Errors + +Class: C + +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 + +You called C 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 + +This exception is thrown when the sequence generation code throws any +sort of error besides C. This could be because your +code explicitly threw an error (that is, Cd), or because it +otherwise encountered an exception (any runtime error). + +This exception has one method, C, which returns the +original C<$@> that was trapped by the Iterator object. This may be a +string or an object, depending on how C was invoked. + +As a string, this exception evaluates to the stringified C<$@>. + +=item * I/O Errors + +Class: C + +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, 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 + +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 + +=head1 SEE ALSO + +I, Mark Jason Dominus, Morgan Kauffman 2005. + +L + +=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 diff --git a/t/doc.t b/t/doc.t new file mode 100644 index 0000000..5bc33c2 --- /dev/null +++ b/t/doc.t @@ -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}); +} diff --git a/t/iappend.t b/t/iappend.t new file mode 100644 index 0000000..679ccfd --- /dev/null +++ b/t/iappend.t @@ -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}); + diff --git a/t/iarray.t b/t/iarray.t new file mode 100644 index 0000000..bd4532f --- /dev/null +++ b/t/iarray.t @@ -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}); + diff --git a/t/ihead.t b/t/ihead.t new file mode 100644 index 0000000..a2f5d7b --- /dev/null +++ b/t/ihead.t @@ -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.}); diff --git a/t/ilist.t b/t/ilist.t new file mode 100644 index 0000000..a679ebb --- /dev/null +++ b/t/ilist.t @@ -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}); + diff --git a/t/imap.t b/t/imap.t new file mode 100644 index 0000000..a26e161 --- /dev/null +++ b/t/imap.t @@ -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}); + diff --git a/t/ipairwise.t b/t/ipairwise.t new file mode 100644 index 0000000..6c935ea --- /dev/null +++ b/t/ipairwise.t @@ -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}); + diff --git a/t/irange.t b/t/irange.t new file mode 100644 index 0000000..653ea1f --- /dev/null +++ b/t/irange.t @@ -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}); + diff --git a/t/iskip.t b/t/iskip.t new file mode 100644 index 0000000..0936559 --- /dev/null +++ b/t/iskip.t @@ -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.}); +