用于创建$ class-> next()方法的哈希的“shift”等价物

时间:2011-09-27 17:16:29

标签: perl

我几乎想说“这是我了!”。

无论如何,我们走了。

我喜欢使用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 }
}

有更好的方法吗?我在疯狂吗?

编辑:

我尝试了池上的建议。当然,Ikegami的例子完美无瑕。当我尝试抽象一点时,所有暴露给对象的东西都是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()循环的第一步。

我显然在这里遗漏了一些东西......

4 个答案:

答案 0 :(得分:4)

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中关于领带哈希的部分也有这样的例子。

答案 1 :(得分:4)

如果你不想依赖哈希的内置迭代器(由每个,键和值使用),那就没有什么可以阻止你制作自己的了。

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

答案 2 :(得分:3)

以下是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;

答案 3 :(得分:1)

好吧,如果您以后不需要$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循环。