Beefy Boxes and Bandwidth Generously Provided by pair Networks
Your skill will accomplish
what the force of many cannot
 
PerlMonks  

Array iterator factory

by Roy Johnson (Monsignor)
on Mar 16, 2005 at 18:24 UTC ( #440072=snippet: print w/ replies, xml ) Need Help??

Description: Inspired by the request in Foreach Loops and finding Array::Iterator modules not to my liking, I whipped up a little array iterator factory. It gave me a reason to do a little dabbing with pod, a little dabbling with warnings. I feel enriched.

Updates: incorporated suggestions from ihb regarding function call syntax and warnings.

use strict; 
use warnings;

=pod

=head1 NAME

array_iterator - a factory for iterators

=head1 SYNOPSIS

  my $iter = array_iterator(@array);
  while (my ($i, $data) = $iter->()) { # was &$iter, see ihb's respons
+e
      # $i is index, $data is data
  }
  $iter->(0);    # Reset to start of array
  $iter->(-1);   # Reset to end of array, and iterate backwards
  $iter->(5);    # Reset $i to 5

=head1 NOTES

  This is an accessor only. It will not create array elements. You can
  do that by operating directly on the array.

  After running out, it implicitly resets to front or back end of the
  array, depending on which direction it was iterating.

=cut

sub array_iterator (\@) {
    my $aref = shift;
    my $i = 0;
    sub {
        if (@_) {
            # no need for use warnings::register;
            $i = shift;
            if ($i > $#$aref) {
                $i = $#$aref;
                warnings::warnif(misc => "index beyond end of array");
            }
            elsif ($i < -@$aref) {
                $i = -@$aref;
                warnings::warnif(misc => "array index excessively nega
+tive");
            }
        } elsif ($i >= -@$aref and $i <= $#$aref) {
            my $i_was = $i;
            $i += $i < 0 ? -1 : 1;
            ($i_was, $aref->[$i_was]);
        } else{
            $i = ($i > $#$aref) ? 0 : -1;
            return ();
        }
    }
}

my @arr = (20..30);
my $I1 = array_iterator(@arr);

print "Skipping every 4th...\n";

while (my ($i, $data) = &$I1) {
    next if $i % 4 == 3;
    print "$i: $data\n";
}

print "Running through again, without skips:\n";
while (my ($i, $data) = $I1->()) { # was &$I1 here and below
    print "$i: $data\n";
}


print "-- Now...backwards! --\n";

$I1->(-1);
while (my ($i, $data) = $I1->()) {
    print "$i: $data\n";
}

print "again, but only printing every 3rd:\n";
while (my ($i, $data) = $I1->()) {
    print "$i: $data\n" if $i % 3 == 0;
}

print "Boundary check:\n";
$I1->(-50);
while (my ($i, $data) = $I1->()) {
    print "$i: $data\n";
}

print "This should print nothing:\n";
my @empty = ();
my $I2 = array_iterator(@empty);
print join(':', $I2->()), "\n";
Comment on Array iterator factory
Download Code
Re: Array iterator factory
by tlm (Prior) on Mar 16, 2005 at 18:54 UTC

    Just a minor point: using a (\@) prototype in the definition of array_iterator, as opposed to just using @_, rules out uses like array_iterator(1..10). Is there a particular benefit of using a prototype here that you are after?

    the lowliest monk

      If I took a list, I'd have to make and store a copy of its contents. It would be expensive, and it would not see subsequent changes to the original array. I wanted a lightweight iterator tied to a user-modifiable array.

      The user can create an anonymous array and slap an iterator on it, if he so desires: array_iterator(@{[1..10]}). That makes it obvious that this iterator can't give you the free lunch that for (1..10) does.


      Caution: Contents may have been coded under pressure.

        Don't forget the power of &array_iterator([1..10]).

        - tye        

Re: Array iterator factory
by ihb (Deacon) on Mar 17, 2005 at 12:02 UTC

    Nice. I like this function more than the array iterator classes I've seen.

    The &$iter; syntax in the documentation is evil though. If you use it it will break things sooner or later. It's better to change them all to $iter->(). (See perlfaq7, "What's the difference between calling a function as &foo and foo()?")

    ihb

    See perltoc if you don't know which perldoc to read!

      Good point. I was not aware of that. Code is updated accordingly.

      Caution: Contents may have been coded under pressure.

      Note that &$iter() is just fine (it has none of the problems of &$iter;) and is even better than $iter->() in that it works on fairly old versions of Perl (though enough time has passed since the $iter->() syntax was introduced that few need to worry much about supporting versions of Perl that predate it).

      It seems that 'everyone' focuses on the & as being the problem and never mention that ampersand with parens is okay, sometimes even an advantage. Note that the both $iter->() and &$iter() ignore prototypes (since such are only enforced at compile time, a time during which perl doesn't know what function $iter might point to) and neither can be used directly with built-in functions so not even the difference between func() and &func() apply between &$iter() and $iter->().

      (tye)Re: A question of style covers much of & and () and calling subroutines.

      - tye        

        It seems that 'everyone' focuses on the & as being the problem and never mention that ampersand with parens is okay, sometimes even an advantage.

        Because I've repeatedly pointed out the issue with &foo; I take the easy way in my replies and don't mention &foo() as the alternative. Instead of elaborating on the same issue once again I'll quote myself. From Re: Hash values and constants:

        I intentionally avoided mentioning & as a way of disambiguate the constant. This is because it will confuse and mislead people to think that you can do &foo; instead of foo(), which isn't the case.
        From Re: Re: Forward-referenceing subs:
        For some reason, it's much more common to drop the parenthesis when dereferencing than when making a regular subroutine call.
        Therefore I consistently use $foo->() as it doesn't leave room for such subtle unexpected behaviour as &$foo; presents.

        ihb

        See perltoc if you don't know which perldoc to read!

Back to Snippets Section

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: snippet [id://440072]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others browsing the Monastery: (2)
As of 2014-09-17 23:55 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    How do you remember the number of days in each month?











    Results (101 votes), past polls