Perl 5-迭代器

Perl 5-迭代器,perl,functional-programming,iterator,perl-module,Perl,Functional Programming,Iterator,Perl Module,我用perl实现了一个简单的迭代器。我通常使用C#,经常使用迭代器和函数编程。所以我认为用perl进行一些基础工作会很简单 问题是,我的性能很差,我不希望比for或foreach更快,但我想有人可以给我一些关于如何加速的见解 以下是我的包裹: package Iterator; use strict; #Constructor for Iterator type sub new { my $type = $_[0]; my $this = {}; #set default val

我用perl实现了一个简单的迭代器。我通常使用C#,经常使用迭代器和函数编程。所以我认为用perl进行一些基础工作会很简单

问题是,我的性能很差,我不希望比for或foreach更快,但我想有人可以给我一些关于如何加速的见解

以下是我的包裹:

package Iterator;
use strict;

#Constructor for Iterator type
sub new {
  my $type = $_[0];
  my $this = {};

  #set default values
  $this->{Array} = @_[1];
  $this->{Index} = 0;
 $this->{Sub} = sub { 
   my @array = @{$this->{Array}};
   return $#array >= $this->{Index} ? $array[$this->{Index}++] : undef;
  };

  #return self
  bless($this, $type);
  return $this;
}

#Iterates next
sub Next {
 return $_[0]->{Sub}->();
}
允许您执行以下操作:

my $iterator = Iterator->new(\@array);
while (defined(my $current = $iterator->Next())) {
  print $current, "\n";
}
不浮华。。。然而

还可以启用以下功能代码:

my $sum = 0;
Iterator
  ->new(\@array)
  ->Where(sub { $_[0] % 2 == 0 })
  ->ForEach(sub { $sum += $_[0] });
将数组的所有偶数相加

我的瓶颈是迭代代码:

$this->{Sub} = sub { 
   my @array = @{$this->{Array}};
   return $#array >= $this->{Index} ? $array[$this->{Index}++] : undef;
  };

有没有加快速度的指针?

你可能会发现读一点很有用。

这一行:

my @array = @{$this->{Array}};

将数组复制到@array中,我认为您不想这样做。只需执行
$#{$this->{Array}}
即可找到数组的端点。

使用
grep
和以下命令,对偶数求和更容易:


在我看来,您的问题所建议的代码很可能过于工程化。除非你能想出一个使用传统Perl原语无法轻松解决的好例子。

一个更有效的版本:

package Iterator;
use strict;

#Constructor for Iterator type
sub new {
  my $type = shift;
  my $array = shift;
  my $this = {};
  $this->{array} = $array;
  $this->{index} = 0;
  bless($this, $type);
  return $this;
}

#Iterates next
sub Next {
  my $this = shift;
  return $this->{array}->[$this->{index}++];
}

更简单的Perl迭代器:

my @array = (1, 2, 3, 4);
while (my $i = shift @array)
{
    print $i . "\n";
}
请查看和以获取可能对您有所帮助的实用程序。 您甚至可以使用更现代的语法

例如:

use perl5i::2;

my @nums = (0..100);
my $sumEven = @nums->grep(sub { $_ % 2 == 0 })->reduce(sub { $a+$b });

say $sumEven;
在CPAN中已经有了一个方法,所以如果您还没有这样做,您可以查看它的方法

顺便说一下,在您的代码中,您有:

#set default values
$this->{Array} = @_[1];
我想你应该说
$\u1]
。使用
@[1]
可以请求一个元素的数组片。最后,结果是相同的,但语义不同。奇怪的是,如果我执行@1]或出现错误,但在调试器中进行了测试,并且您获得了标量(至少在perl 5.10中),那么我希望得到一个包含一个元素的数组。Perl 6无论如何都会支持这种行为,并且不会更改访问数组或散列中元素的sigil,因此您正在编写“高级”Perl;-)

不要卸载存储的阵列。执行以下操作时,您正在将数组的每个元素从
$this->{array}
指向的位置复制到本地列表
@array

my @array = @{$this->{Array}};
另外,如果您知道在点击
undef
时将停止,那么您甚至不必检查边界

$this->{Sub} = sub { return $this->{Array}[++$this->{Index}]; }
这就是你所需要的。当
{Index}
超出范围时,它将返回
undef

此外,您可以用Perl编写表达式,如:

$sum += $_ foreach grep { $_ % 2 == 0 } @array;

这里的游戏有点晚了,但由于您关心性能,迭代器类型代码中最大的瓶颈之一是每次访问时都需要取消对基于哈希的对象字段的引用。解决这一问题的一种方法是使用闭包,其中字段在变量上是闭合的,从而避免不必要的去引用

在我的模块中,包含了一个性能相当好的惰性列表实现,我编写了实用函数
curse
,它使基于闭包的对象的行为类似于普通的Perl对象

下面是一个用
curse
编写的迭代器类的简短示例。在对1000个数字求和的简单基准测试中,此方法的速度是您的两倍,即使在解决了其他答案中指出的所有低效问题之后也是如此

{package Iterator;
    use List::Gen 'curse';
    sub new {
        my ($class, $array) = @_;
        my $index = 0;
        curse {
            next  => sub {$$array[$index++]},
            index => sub :lvalue {$index},
        } => $class
    }
    sub reset {shift->index = 0}
} 
如果你真的在追求更快的速度,因为
next
方法不需要传递任何东西,你甚至可以写:

my $next = $iterator->can('next');

while (defined (my $x = $next->()) {...}
这将使您的速度比普通方法调用提高30%到40%


您可以阅读
List::Gen
的源代码,了解
curse

的更高级用法。与常规
map
grep
相比,此解决方案的预期优势是什么?我正在做一些复杂的算法(装箱问题),函数编程非常有帮助。将普通的foreach转换成类似于上面的代码已经减少了100行代码(大约33%)。还有助于可维护性,因为它迫使您的SUB更加纯粹(没有全局变量)。函数式编程非常棒。是什么让你认为
map
grep
和company不是函数式编程?我可能错了,但在我看来,您只是在用OO样板来包装已经在工作的解决方案。您正在用perl编写OO模块,以生成功能接口。真正地zoul说得对,map和grep等人更适合。请不要试图让perl看起来像C#。FWIW:中有用C编写的数组迭代器可供使用。我已经看到,尽管快速浏览PDF,但没有找到更好的数组迭代器实现。@Jonathon.Peppers,因为perl已经有了一个。这是呼叫值班。好多了!,如果我们不能从中获得更多的性能,我会把你作为答案。我想我给你的问题太简单了。虽然我觉得我们有点离题了。我们可以把10个开发人员放在一个房间里一个小时,就函数式编程提出12种不同的意见。我不认为这有什么帮助,因为我的问题是如何优化我的迭代器类(这类代码行数很少,因此在它上面使用现有的perl包几乎没有什么好处)。@Jonathan Peppers,问题是,如果你把10个Perl开发人员放在一个房间里,他们中没有一个会接近你所做的。@Jonathon:我只是想在编写自己的Perl构建块之前确保你了解传统的Perl构建块,因为优化一段代码的最佳方法就是去掉它。@Jonathon.Peppers,与语言作对会对你的表现、生产力和学习造成伤害。仅仅因为Perl是很久以前创建的,并不意味着它的编程没有发展。如果你愿意,你可以继续看不起Perl程序员。但是他们中没有一个人会像你写的那样写一篇很慢的废话。我不想在这里过于苛刻。但是你不应该批评那些你不喜欢的人
my $next = $iterator->can('next');

while (defined (my $x = $next->()) {...}