我几乎想说"又是我!"
不管怎样,我们开始吧。
我喜欢使用while $object->next()
风格的结构。它们对我很有吸引力,看起来很"整洁"。
现在,当我要迭代的对象是一个数组时,它很简单(" shift @ary or return undef
")
sub next {
my ( $self, $args ) = @_;
my $next = shift @{ $self->{list_of_things} } or return undef;
my ( $car, $engine_size, $color )
= split( /Q$opts->{fieldsep}/, $next );
$self->car = $host;
$self->engine_size = $engine_size;
$self->color = $color;
}
在这个例子中,我使用AUTOLOAD来创建getter和setter,然后在while
循环期间在我的对象中使用这些实例变量。
我想做一些类似的事情,但"list_of_things"是一个%hash
。
这是一个没有进入第一次迭代的非oo示例。知道为什么吗?
(总的"list_of_things"不是那么大——可能有100个条目——所以每次做一个keys(%{$hash})
对我来说似乎并不太浪费)。
use strict;
use warnings;
use Data::Dumper;
my $list_of_things = {
volvo => {
color => "red",
engine_size => 2000,
},
bmw => {
color => "black",
engine_size => 2500,
},
mini => {
color => "british racing green",
engine_size => 1200,
}
};
sub next {
my $args = $_;
my @list = keys( %{$list_of_things} );
return undef if scalar @list == "0";
my $next = $list_of_things->{ $list[0] };
delete $list_of_things->{ $list[0] };
return $next;
}
while ( next()) {
print Dumper $_;
print scalar keys %{ $list_of_things }
}
有更好的方法吗?我是不是做了什么疯狂的事?
<标题>编辑:我尝试了池上的建议。当然,池上的例子是完美的。当我尝试抽象一点,以便所有暴露给对象的都是next->()方法时,我得到了与原始示例相同的"perl-going-to-100%-cpu"问题。
下面是一个非oo的例子:
use Data::Dumper qw( Dumper );
sub make_list_iter {
my @list = @_;
return sub { @list ? shift(@list) : () };
}
sub next {
make_list_iter( keys %$hash );
}
my $hash = { ... };
while ( my ($k) = next->() ) {
print Dumper $hash->{$k};
}
它似乎没有通过while()循环的第一步。
标题>如果你不想依赖哈希的内置迭代器(每个键和值都使用),没有什么可以阻止你自己创建。
use Data::Dumper qw( Dumper );
sub make_list_iter {
my @list = @_;
return sub { @list ? shift(@list) : () };
}
my $list_of_things = { ... };
my $i = make_list_iter(keys %$list_of_things);
while (my ($k) = $i->()) {
local $Data::Dumper::Terse = 1;
local $Data::Dumper::Indent = 0;
say "$k: " . Dumper($list_of_things->{$k});
}
each
操作符是一个内置的遍历哈希值的操作符。当返回的元素用完时返回undef
。所以你可以输入
package SomeObject;
# creates new object instance
sub new {
my $class = shift;
return bless { hash_of_things => { @_ } }, $class
}
sub next {
my $self = shift;
my ($key,$value) = each %{ $self->{hash_of_things} };
return $key; # or return $value
}
对哈希值调用keys
将重置each
迭代器。知道这一点很好,这样你就可以故意重置它:
sub reset {
my $self = shift;
keys %{ $self->{hash_of_things} }
}
,这样你就可以避免在意外情况下重置它。
在perltie
中绑定哈希的部分也有一个这样的例子
下面是如何使用List::Gen
从列表创建迭代器:
use strict;
use warnings;
use List::Gen 'makegen';
my @list_of_things = ( # This structure is more suitable IMO
{
make => 'volvo',
color => 'red',
engine_size => 2000,
},
{
make => 'bmw',
color => 'black',
engine_size => 2500,
},
{
make => 'mini',
color => 'british racing green',
engine_size => 1200,
}
);
my $cars = makegen @list_of_things;
print $_->{make}, "n" while $cars->next;
如果你以后不需要$list_of_things
,你总是可以这样做
while(keys %$list_of_things)
{
my $temp=(sort keys %$list_of_things)[0];
print "key: $temp, value array: " . join(",",@{$list_of_things->{$temp}}) . "n";
delete $list_of_things->{$temp};
}
如果你确实需要它,你总是可以将它分配给一个临时哈希引用,并对它执行相同的while
循环。