Python – the Perl version of a Python iterator

iteratorperlpython

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…

Best Answer

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 );