Beefy Boxes and Bandwidth Generously Provided by pair Networks
Problems? Is your data what you think it is?
 
PerlMonks  

iter

by MeowChow (Vicar)
on Mar 27, 2001 at 21:50 UTC ( [id://67560]=perlcraft: print w/replies, xml ) Need Help??

   1: #!/usr/bin/perl -w
   2: #
   3: # Q: Perl need iterator? That's unpossible!
   4: # A: Have you ever had to iterate on multiple list values at once? One 
   5: #    often resorts to numeric indexing to solve this problem. Shift and splice
   6: #    is another WTDI, but only if you don't mind eating away at your original
   7: #    list. Think of the iterator as a non-mutating splice.
   8: #
   9: # iterstart ARRAY VARIABLE
  10: # iterstart ARRAY
  11: #   Sets up iteration for a given array, with the iterator variable
  12: #   specified. Must be called before any of the other functions.
  13: #
  14: # iter NUM ARRAY
  15: # iter NUM
  16: # iter
  17: #   Iterates through and returns a list of next NUM elements for the 
  18: #   active or specified array. A negative NUM acts as an index from the
  19: #   end of the array. When NUM is unspecified, it is defaulted as 1.
  20: #
  21: # iterend ARRAY
  22: # iterend
  23: #   End iteration for active or specified array. Not usually
  24: #   needed.
  25: #
  26: use strict;
  27: {
  28:   my (%listpos, $lref);
  29: 
  30:   sub iterstart (\@) {
  31:     $lref = shift;
  32:     $listpos{$lref} = 0;
  33:   }
  34: 
  35:   sub iter (;$\@) {
  36:     my $num = shift || 1;
  37:     $lref = shift if @_;
  38:     return if not exists $listpos{$lref};
  39:     my $i = $listpos{$lref};
  40:     my $j = $num >= 0 ? $i + $num - 1: $#$lref + $num + 1;
  41:     if ($j < $#$lref) {
  42:       $listpos{$lref} = $j + 1;
  43:     }
  44:     else {
  45:       delete $listpos{$lref};
  46:       $j = $#$lref;
  47:     }
  48:     @$lref[$i..$j];
  49:   }
  50: 
  51:   sub iterend (;\@) {
  52:     delete $listpos{shift || $lref}
  53:   }
  54: }
  55: 
  56: ############ EXAMPLES ############
  57: 
  58: use Data::Dumper;
  59: my (@data, @lol);
  60: 
  61: # Example: iterate over a multi-value list
  62: @data = qw(1 one uno 2 two dos 3 three tres 4 four quatro 5 five cinco);
  63: iterstart @data;
  64: while (my ($num, $eng, $span) = iter 3) {
  65:   print "$num is $eng in English and $span in Spanish\n";
  66: }
  67: 
  68: # Example: convert a flat data structure to a list-of-lists
  69: @data = qw(1 fee 2 fee fi 3 fee fi fo 4 fee fi fo fum 1 fin);
  70: iterstart @data;
  71: while (my ($len) = iter) {
  72:   push @lol, [iter $len];
  73: }
  74: print Dumper \@lol;

Replies are listed 'Best First'.
Re: iter
by Dominus (Parson) on Mar 27, 2001 at 22:12 UTC
    It seems to me that your solution has two problems.

    First, you cannot have more than one active iterator in your program at once.

    Second, you used a lot of code!

    You might prefer this object-oriented approach. The interface is almost the same as yours, but the code is only six lines long:

    sub iterstart { my @a = @_; return sub { my $n = shift; $n = 1 unless defined $n; return splice @a, 0, $n; }; } @data = qw(1 fee 2 fee fi 3 fee fi fo 4 fee fi fo fum 1 fin); my $iter = iterstart @data; while (my ($len) = $iter->()) { push @lol, [$iter->($len)]; } use Data::Dumper; print Dumper \@lol;
      Actually you can have more than one active iterator; however in that case, you should explicitly specify the iterating list when calling iter. I avoided the splice-based solution because I didn't want to add the overhead of copying the array. In truth, I'm not sure how much performance this buys me, considering all the splice-ish code which is now duplicated in perl. I also prefered not to take the OO aproach because I wanted a function that would feel more like a built-in (like each, for example).

      That said, my solution is pretty ugly in comparison :)

      update: After running some benchmarks, it's apparent that my solution is less efficient (by a factor of about 7x when iterating on each individual item), since the built-in splice is considerably faster than a re-implementation of a non-destructive splice in Perl. (though, my iter does outperform the OO iter when iterating and returning segments of length 100 or greater {really big grin})

      So without further ado, -- this quote-unquote craft, and forget I ever wrote this piece of schnapz.

      update2: Oh yeah, I forgot to mention, you wrote alot of code! :p

      sub iter { my @a = @_; sub { splice @a, 0, shift || 1 }; }
        Says meowchow:
        sub { splice @a, 0, shift || 1 };
        If you do that, then $iter->(0) doesn't work properly.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others perusing the Monastery: (6)
As of 2024-04-24 06:51 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found