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

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

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

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

一个例子:一个经典的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 

迭代器也是有用的,如果你想根据需要生成一个更大的列表子部分。 Perl'lists'看起来更加静态 – 更像是一个Python元组。 在Perl中,可以foreach是dynamic的还是只基于静态列表?

Iterator的Pythonforms是一种我已经习惯的forms,我没有发现它在Perl中logging…除了写入循环或recursion或产生一个巨大的静态列表,我如何(为前)写在Perl中的斐波那契子程序? 有没有我失踪的Perl yield

具体来说 – 我怎么写这个:

 #!/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"; } 

预先感谢对新手的善良…

对于比Python生成器更灵活的解决scheme,我已经在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 

由于发生器假装是arrays,他们可以无缝地与普通的Perl代码混合。 还有一个面向对象的方法:

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

在每种情况下,生成器都是懒惰的,在创build时不计算任何值,然后只计算满足切片所需的值。 Fibonacci序列的recursion定义会自行调用多次,因此cache函数用于确保每个值只计算一次。

你也可以使用生成器作为迭代器:

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

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

如果您有任何问题,请告诉我。

更新:

我已经发布了模块的新版本(0.80),使得在发生器中使用迭代algorithm变得更加容易。 下面是一个与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}; 

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

 say for @{fibonacci 400}; # or more 

Perl中的迭代器的概念有点不同。 你基本上想要在持久variables上返回一个“closures”的子程序。

 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++; } 

如果你不喜欢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 ); 

优秀的高级Perl书(在指定的链接上免费提供)包含大量相关主题的信息,特别是关于迭代器的整章。 作者认为,“高阶”意味着使用Perl的function作为一种function性的语言,具有一stream的function,可以实现各种酷炫的东西。 这真的是一本非常好的书 – 我读了大部分,关于迭代器和stream的章节都非常棒。 如果您打算编写Perl代码,我强烈推荐至less浏览一下。

有一个类似的方法来生成一个迭代器/生成器,但它不是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; } 

sub fibo维护一个允许持久variables被维护的Perl 闭包 。 你可以做一个模块,类似于C / C ++。 在fibo里面,一个匿名子例程完成返回下一个数据项的工作。

引用Perl的圣经 “你将会感到悲惨,直到你了解标量与列表上下文之间的区别” – 第69页(强烈推荐的书btw …)

在这种情况下,annon子只返回一个值。 我所知道的Perl中唯一可以在标量上下文中运行的循环机制是while ; 其他人试图在继续之前填写清单,我想。 因此,如果你在列表上下文中调用匿名子,它将尽职尽责地返回下一个斐波那契数,不像Python的迭代器,循环将会终止。 这就是为什么我把这个return undef if .... wantarray因为它不能在列表上下文中写作。

有办法解决这个问题。 事实上,你可以编写像map foreach这样的子程序,但是它不像Python的yield那样直截了当。 您将需要一个额外的function在foreach循环内使用。 权衡是Perl方法具有巨大的力量和灵活性。

您可以在Mark Jason Dominus的“高级Perl”高级书籍中阅读关于Perl迭代器的更多信息。 第4章讲述Interators brian d foy在Perl Review中也有一篇关于Interators的优秀文章 。

这里有一个很好的实际例子和一篇PDF文章…但是我在Perl中太生疏了,试图直接实现你的挑战(正如你将会看到的,PDF中的例子和方法都使用了一个不那么直接的方法)。

这是一个专门针对最初提出的问题量身定做的回应。

任何实现了惰性列表的Perl模块(例如List :: Gen,Memoize等等)也可以让你提供你自己的生成器子程序(我不是指像Python那样的“generator”),你可以像这样做例。 这里懒惰地生成列表的模块叫做Alef。

 #!/usr/bin/perl -w use strict; use warnings; use Alef; my $fibo; BEGIN { my ($a, $b) = (0, 1); $fibo = sub { ($a, $b) = ($b, $a+$b); $a; } } my $fibonacci = new Alef($fibo); foreach my $number ($fibonacci->take(15)){ print $number . "\n"; } 

这是输出:

[spl @ briareus〜] $ ./fibo.pl 0 1 1 2 3 5 8 13 21 34 55 89 144 233 377

在这里使用懒惰列表模块的背后没有任何神奇的事情发生。 这就是Alef的子程序的样子。

 sub take { my ($self,$n) = (@_); my @these = (); my $generator = $self->{'generator'}; for (1..$n){ push(@these,$self->{'this'}); $self->{'this'} = &$generator($self->{'this'}); } @these; } 

在这种情况下,可以使用记忆。

 use strict; use warnings; use Memoize; memoize('fib'); foreach my $i (1..15) { print "$i -> ",fib($i),"\n"; } sub fib { my $n = shift; return $n if $n < 2; fib($n-1) + fib($n-2); } 

在CPAN上有几个迭代器/发生器模块可以帮助您。 以下是您直接转换到Coro::Generator模块的示例:

 use 5.016; use warnings; use Coro::Generator; sub gen_fibonacci { my $mag = shift; generator { my ($a, $b) = (0, 1); while ($a <= 10 ** $mag) { yield $a; ($a, $b) = ($b, $a + $b); } yield undef; # stop it! }; } my $fibonacci = gen_fibonacci(15); while (defined (my $number = $fibonacci->())) { printf "%17d\n", $number; }