Perl 5 - 迭代器
我在 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;
};
有什么可以加快速度的指针吗?
I have implemented a simple iterator in perl. I normally work with C#, and use iterators and functional programming quite frequently. So I thought it would be simple to get some basics working in perl.
Problem is, I'm getting some poor performance, I don't expect be any faster than for or foreach, but I thought someone could give me some insight in how to speed it up.
Here is the guts of my package:
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}->();
}
Allows you to do this:
my $iterator = Iterator->new(\@array);
while (defined(my $current = $iterator->Next())) {
print $current, "\n";
}
Not flashy... yet.
Also enables some functional code like this:
my $sum = 0;
Iterator
->new(\@array)
->Where(sub { $_[0] % 2 == 0 })
->ForEach(sub { $sum += $_[0] });
Which would sum up all the even values of an array.
My bottleneck is the iteration code:
$this->{Sub} = sub {
my @array = @{$this->{Array}};
return $#array >= $this->{Index} ? $array[$this->{Index}++] : undef;
};
Any pointers to speed this up?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(9)
这里的游戏有点晚了,但由于您关心性能,迭代器类型代码中最大的瓶颈之一是基于哈希的对象的字段需要在每次访问时取消引用。解决这个问题的一种方法是使用闭包,其中字段在变量上封闭,避免不必要的取消引用。
在我的模块 List::Gen 中,其中包含惰性列表的相当高性能的实现,我编写了实用函数
curse
,它使基于闭包的对象的行为与普通 Perl 对象一样。下面是一个用
curse
编写的迭代器类的简短示例。在对 1000 个数字求和的简单基准测试中,即使在修复了其他答案中指出的所有低效率问题之后,此方法的速度还是您的方法的两倍。如果您确实想要提高速度,由于
next
方法不需要传递任何内容,您甚至可以这样写:与普通方法调用相比,这将使您的速度提高 30% 到 40% 。
您可以阅读
List::Gen
的源代码以了解curse
的更高级用法A bit late to the game here, but since you are concerned about performance, one of the largest bottlenecks in iterator type code is that the fields of your hash based object need to be dereferenced on each access. One way to combat this is to use closures in which the fields are closed over variables, avoiding unneeded dereferencing.
In my module List::Gen which contains a fairly performant implementation of lazy lists, I wrote the utility function
curse
which makes closure based objects behave like normal Perl objects.Here is a short example of your iterator class written with
curse
. In a simple benchmark summing 1000 numbers, this method is twice as fast as yours, even after fixing all of the inefficiencies noted in the other answers.If you are really pushing for more speed, since the
next
method does not need to be passed anything, you could even write:Which will give you a 30% to 40% speed boost over a normal method call.
You can read the source of
List::Gen
for more advanced usage ofcurse
您可能会发现阅读一些高阶 Perl 很有用。
You might find it useful to read a bit of Higher Order Perl.
这一行:
将数组复制到@array中,我认为你不想这样做。只需执行
$#{$this->{Array}}
即可找到数组的端点。this line:
duplicates the array into @array, and I don't think you want to. Just do
$#{$this->{Array}}
to find the endpoint of your array.一个更有效的版本:
A much more efficient version:
使用
grep
和List 可以更轻松地对偶数求和: :Util
:在我看来,你的问题建议的代码很可能是过度设计的。除非你能想出一个使用传统 Perl 原语无法轻松解决的像样的例子。
Summing even numbers is easier done using
grep
andList::Util
:It seems very likely to me that that the code suggested by your question is over-engineering. Unless you can come up with a decent example that cannot be easily solved using the traditional Perl primitives.
看看 List::Util 和 List::MoreUtils 可以帮助您解决此问题。
您甚至可以使用 perl5i 获得更现代的语法。
例子:
Have a look at List::Util and List::MoreUtils for utilities that may help you with this.
You can even use perl5i for a more modern looking syntax.
Example:
已经有一个 数组迭代器 CPAN,所以如果你还没有做过的话可以看看它的方法。
顺便说一句,在您的代码中:
我假设您想说
$_[1]
。使用@_[1]
您正在请求一个元素的数组切片。最后的结果是相同的,但语义不同。奇怪的是,如果我执行 @_[1] 或出现错误,我期望得到一个包含一个元素的数组,但在调试器中进行了测试,并且您获得了标量(至少在 perl 5.10 中)。无论如何,Perl 6 都会采用这种行为,并且不会更改用于访问数组或散列中的元素的印记,因此您正在编写“高级”Perl ;-)There is already an array iterator in CPAN, so you can look at its approach if you have not done it yet.
By the way in your code you have:
I assume you want to say
$_[1]
. With@_[1]
you are requesting an array slice of one element. At the end the result is the same but the semantics isn't. The curious thing is that I was expecting to have an array of one element if I do @_[1] or an error but tested in the debugger and you obtain the scalar (at least in perl 5.10). Perl 6 will go for this behaviour anyway and will not change sigil for accessing elements in arrays or hashes so you are coding 'advanced' Perl ;-)不要卸载存储的数组。当您执行以下操作时,您会将数组的每个元素从
$this->{Array}
指向的位置复制到本地列表@array
:另外如果您知道当您点击
undef
时您将停止,那么您甚至不必检查边界。这就是您所需要的。当
{Index}
超出范围时,将返回undef
。此外,您可以用 Perl 编写表达式,如下所示:
Don't unload the stored array. You're copying every element of an array from where it is pointed at by
$this->{Array}
to the local list@array
when you do this:Also if you know that you are going to stop when you hit
undef
, then you don't have to even check bounds.Is all you need. When
{Index}
gets out of range, it will returnundef
.In addition, you can write your expression in Perl like:
一个更简单的 Perl 迭代器:
A much simpler Perl iterator: