Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the Perl version of a Python iterator?

I am learning Perl at my work and enjoying it. I usually do my work in Python but boss wants Perl.

Most of the concepts in Python and Perl match nicely: Python dictionary=Perl hash; Python tuple=Perl list; Python list=Perl array; etc.

Question: Is there a Perl version of the Python form of an Iterator / Generator?

An example: A Classic Python way to generate the Fibonacci numbers is:

#!/usr/bin/python  def fibonacci(mag):      a, b = 0, 1      while a<=10**mag:          yield a          a, b = b, a+b  for number in fibonacci(15):        print "%17d" % number 

Iterators are also useful if you want to generate a subsection of a much larger list as needed. Perl 'lists' seem more static - more like a Python tuple. In Perl, can foreach be dynamic or is only based on a static list?

The Python form of Iterator is a form that I have gotten used to, and I do not find it documented in Perl... Other than writing this in loops or recursively or generating a huge static list, how do I (for ex) write the Fibonacci subroutine it in Perl? Is there a Perl yield that I am missing?

Specifically -- how do I write this:

#!/usr/bin/perl use warnings; use strict; # yes -- i use those!  sub fibonacci {    # What goes here other than returning an array or list?  }  foreach my $number (fibonacci(15)) { print $number . "\n"; } 

Thanks in advance to being kind to the newbie...

like image 491
the wolf Avatar asked Sep 23 '10 04:09

the wolf


People also ask

What is a Python iterator?

An iterator is an object that contains a countable number of values. An iterator is an object that can be iterated upon, meaning that you can traverse through all the values. Technically, in Python, an iterator is an object which implements the iterator protocol, which consist of the methods __iter__() and __next__() .

What is next ITER in Python?

The next() function returns the next item in an iterator. You can add a default return value, to return if the iterable has reached to its end.

How can you know that iterator is done in Python?

An iterator object implements __next__, which is expected to return the next element of the iterable object that returned it, and to raise a StopIteration exception when no more elements are available. In the simplest case, the iterable will implement __next__ itself and return self in __iter__.

What is the difference between iterator and iterable in Python?

Here is a list of the differences between Iterable and Iterator in Python. An Iterable is basically an object that any user can iterate over. An Iterator is also an object that helps a user in iterating over another object (that is iterable). We can generate an iterator when we pass the object to the iter() method.


2 Answers

The concept of an iterator is a little different in Perl. You basically want to return a one-use subroutine "closed" over the persistent variables.

use bigint; use strict; use warnings;  sub fibonacci {     my $limit = 10**( shift || 0 );     my ( $a, $b ) = ( 0, 1 );     return sub {          return if $a > $limit;         ( my $r, $a, $b ) = ( $a, $b, $a + $b );         return $r;     }; } my $fit = fibonacci( 15 ); my $n = 0; while ( defined( my $f = $fit->())) {       print "F($n): $f\n";      $n++; } 

And if you don't like the while loop, then here is two shots at some syntactic sugar, which basically accomplish an each-item loop.:

sub iterate ($$) {     my $iter   = shift;     my $action = shift;     while ( defined( my $nextval = $iter->())) {          local *_ = \$nextval;         $action->( $_ );     }     return; }  iterate fibonacci( 15 ) => sub { print "$_\n"; };  sub iter (&$) {      my $action = shift;     my $iter   = shift;     while ( defined( my $nextval = $iter->())) {          local *_ = \$nextval;         $action->( $_ );     }     return; }  iter { print "$_\n" } fibonacci( 15 ); 
like image 64
Axeman Avatar answered Oct 05 '22 04:10

Axeman


For an even more flexible solution than Python's generators, I have written the module List::Gen on CPAN which provides random access lazy generator arrays:

use List::Gen;  my $fib; $fib = cache gen {$_ < 2  ? $_ : $$fib[$_ - 1] + $$fib[$_ - 2]};  say "@$fib[0 .. 15]";  #  0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 

Since generators pretend to be arrays, they can mix seamlessly with normal perl code. There is also an object oriented approach:

my $fib; $fib = cache gen {$_ < 2 ? $_ : $fib->get($_ - 1) + $fib->get($_ - 2)};  say join ' ' => $fib->slice(0 .. 15); 

In each case, the generator is lazy, calculating nothing upon creation, and then calculating only those values required to satisfy the slices. The recursive definition of the Fibonacci sequence calls itself many times, so the cache function is used to make sure each value is only calculated once.

You can also use generators as iterators:

while (my $num = $fib->next) {     last if $num > 10**15;     print "$_\n"; } 

$fib->next can also be written $fib->(). Since the generator is still random access, you can $fib->reset() or $fib->index = 10;

Let me know if you have any questions.

Update:

I have released a new version of the module (0.80) that makes it easier to use iterative algorithms in generators. Here is an example that closely mirrors the OP's example:

use List::Gen '*';  sub fibonacci {     my $limit   = 10**shift;     my ($x, $y) = (0, 1);      While {$_ < $limit} gather {         ($x, $y) = ($y, take($x) + $y)     } }  say for @{fibonacci 15}; 

if you use bigint; before or at the top of the sub, you can of course:

say for @{fibonacci 400}; # or more 
like image 32
Eric Strom Avatar answered Oct 05 '22 03:10

Eric Strom