什么是Python迭代器的Perl版本?

the*_*olf 39 python perl iterator

我在工作中学习Perl并享受它.我通常用Python做我的工作,但老板想要Perl.

Python和Perl中的大多数概念都很匹配:Python dictionary = Perl hash; Python元组= Perl列表; Python list = Perl数组; 等等

问题:是否有迭代器 /生成的Python形式的Perl版本?

示例:生成Fibonacci数字的经典Python方法是:

#!/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
Run Code Online (Sandbox Code Playgroud)

如果要根据需要生成更大列表的子部分,迭代器也很有用.Perl'列表'似乎更加静态 - 更像是Python元组.在Perl中,可以foreach是动态的还是仅基于静态列表?

Iterator的Python形式是我已经习惯的形式,我没有在Perl中找到它...除了在循环或递归中编写它或生成一个巨大的静态列表,我如何(为ex)写它在Perl中的Fibonacci子程序?是否有yield我错过的Perl ?

具体来说 - 我该怎么写:

#!/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"; }
Run Code Online (Sandbox Code Playgroud)

在此先感谢对新手的善意......

Axe*_*man 37

迭代器的概念在Perl中有点不同.你基本上想要在持久变量上返回一个"使用"子程序"关闭".

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++;
}
Run Code Online (Sandbox Code Playgroud)

如果你不喜欢while循环,那么这里有一些语法糖,这基本上完成了每个项目循环:

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 );
Run Code Online (Sandbox Code Playgroud)

  • 一个很好的答案,但你的限制是错误的.如果你看一下Python它是10**限制,而不只是15步...... (2认同)
  • 为什么`(*)`glob context? (2认同)

Eri*_*rom 34

为了比Python的生成器更灵活的解决方案,我在CPAN上编写了模块List :: Gen,它提供了随机访问延迟生成器数组:

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
Run Code Online (Sandbox Code Playgroud)

由于生成器假装是数组,因此它们可以与普通的perl代码无缝混合.还有一种面向对象的方法:

my $fib; $fib = cache gen {$_ < 2 ? $_ : $fib->get($_ - 1) + $fib->get($_ - 2)};

say join ' ' => $fib->slice(0 .. 15);
Run Code Online (Sandbox Code Playgroud)

在每种情况下,生成器都是惰性的,在创建时不计算任何内容,然后仅计算满足切片所需的那些值.Fibonacci序列的递归定义多次调用自身,因此该cache函数用于确保每个值仅计算一次.

您还可以使用生成器作为迭代器:

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

$fib->next也可以写$fib->().由于生成器仍然是随机访问,您可以$fib->reset()$fib->index = 10;

如果您有任何疑问,请告诉我.

更新:

我发布了一个新版本的模块(0.80),可以更容易地在生成器中使用迭代算法.这是一个与OP的例子密切相关的例子:

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};
Run Code Online (Sandbox Code Playgroud)

如果你use bigint;之前或在子的顶部,你当然可以:

say for @{fibonacci 400}; # or more
Run Code Online (Sandbox Code Playgroud)


Eli*_*sky 16

优秀的高阶Perl书(在指定链接上免费提供)包含大量有关相关主题的信息,特别是有关迭代器的整章.通过"更高阶",作者暗示使用Perl的能力作为具有一流功能的函数式语言来实现各种很酷的东西.它确实是一本非常好的书 - 我阅读了大部分内容,关于迭代器和流的章节非常棒.如果您打算编写Perl代码,我强烈建议至少浏览一下.


daw*_*awg 8

有一种类似的方法来生成迭代器/生成器,但它不像Python上的"一等公民".

在Perl中,如果你没有看到你想要什么(一后强制性旅行CPAN FIRST!),你可以滚你自己类似于基于Perl的封锁和匿名子程序一个Python迭代器.

考虑:

use strict; use warnings;

sub fibo {
    my ($an, $bn)=(1,0);
    my $mag=(shift || 1);
    my $limit=10**$mag;
    my $i=0;

    return sub {
        ($an, $bn)=($bn, $an+$bn);      
        return undef if ($an >=$limit || wantarray );
        return $an;
    }
}

my $num;
my $iter=fibo(15);
while (defined($num=$iter->()) ) { printf "%17d\n", $num; }
Run Code Online (Sandbox Code Playgroud)

该子fibo维护一个Perl 闭包,允许维护持久变量.你可以通过一个类似于C/C++的模块来做同样的事情.在fibo匿名子例程内部执行返回下一个数据项的工作.

引用Perl圣经 "在你学习标量和列表上下文之间的差异之前,你会很痛苦" - 第69页(强烈推荐的书btw ...)

在这种情况下,annon sub仅返回单个值.我在Perl中知道的唯一可以在标量上下文中工作的循环机制是while; 其他人试图填写清单,然后继续我的想法.因此,如果你在列表上下文中调用anon sub,它将尽职地返回下一个fibonacci数,不像Python的迭代器那样,循环将终止.这就是为什么我把return undef if .... wantarray它放在原因,因为它在列表上下文中不起作用.

有办法解决这个问题.实际上,你可以编写像其他类似的子程序,map foreach但它并不像Python的收益那么简单.在foreach循环中需要一个额外的函数才能使用.权衡是Perl方法具有巨大的力量和灵活性.

你可以在Mark Jason Dominus的优秀书籍"高阶Perl"中阅读更多关于Perl迭代器的内容.第4章关于Interators brian d foy也有一篇关于Perl Review中Interators 的优秀文章.


Ale*_*lli 6

有一个很好的实际例子在这里和PDF的文章在这里 ...但我太生疏在Perl尝试直接实现你的挑战(如你所见,这两个例子,在PDF中的方法使用不太直接的方法).