有没有一种方法可以使类重载数组取消引用程序,以便可以向foreach
提供该类的实例,并在每次迭代时运行自定义函数?
我尝试过:
重载@{}
并将其分配给一个子。但是,在这种情况下,子仅执行一次,并且应该返回对要迭代的数组的引用。几乎但不是我想要的。
重载<>
。每次读取尝试都会执行分配的功能。正是我想要的方式,但是它与while
循环一起工作,而不与foreach
一起工作。
overloading.pl
use overloaded;
$class = overloaded->new();
$class->add("Hi");
$class->add("Hello");
$class->add("Yeet");
foreach $string (@$class) {
print("NEXT ITEM: ".$string."\n");
}
while ($item = <$class>) {
print("NEXT ITEM: ".$item."\n");
}
overloaded.pm
package overloaded;
use strict;
use warnings;
use Data::Dumper;
use overload '@{}' => \&next, '<>' => \&fetchNext;
sub new {
my ($class, %args) = @_;
$args{fields} = ();
$args{pos} = 0;
return bless { %args }, $class;
}
sub add {
my ($self, $elem) = @_;
push(@{$self->{fields}}, $elem);
}
sub fetchNext {
my ($self) = @_;
print("reading next line...\n");
return $self->{fields}->[$self->{pos}++];
}
sub next {
my ($self) = @_;
print ("getting next array item\n");
return \@{$self->{fields}};
}
1;
输出:
$ perl overloading.pl
getting next array item
NEXT ITEM: Hi
NEXT ITEM: Hello
NEXT ITEM: Yeet
reading next line...
NEXT ITEM: Hi
reading next line...
NEXT ITEM: Hello
reading next line...
NEXT ITEM: Yeet
reading next line...
答案 0 :(得分:5)
您遇到的问题不是@{...}
和<...>
之间的区别,而是foreach
和while
之间的区别。
while
循环的行为有点像迭代器,就像它的执行一样:
while (you can run a piece of code and get back a value) {
do something
}
因此,每次循环时,它都会在while
条件下执行一段代码,并期望返回单个值。 while
条件下的代码在标量上下文中运行。
另一方面,foreach
循环仅执行一次代码,并期望返回值列表。循环开始时括号之间的代码在列表上下文中执行。
这就是为什么您每次使用以下一行读取一个大文件的原因:
while (<$file_handle>) {
...
}
这一次仅从文件中读取一条记录。如果您像这样使用foreach
循环:
foreach (<$file_handle>) {
...
}
然后,您将立即从文件句柄中取回所有记录-显然,这将占用更多的内存。
解引用数组引用也是如此。您可以同时取回所有值。覆盖的方法(next()
)仅会被调用一次,并有望返回值列表。
答案 1 :(得分:2)
我在Object::Iterate中展示了一些技巧。给您的对象一些方法来提供下一个值,然后从那里开始。
答案 2 :(得分:2)
[这会增加Dave Cross的答案,而不是一个单独的答案]
每个集合只有一个迭代器实例会引起各种问题。例如,这就是每个人都使用keys
而不是each
的原因。因此,我强烈建议您的类生成一个迭代器,而不是一个迭代器,如下所示:
package My::Collection;
use strict;
use warnings;
use Iterator::Simple qw( iarray );
sub new {
my ($class) = @_;
my $self = bless({}, $class);
$self->{fields} = [];
return $self;
}
sub add {
my ($self, $elem) = @_;
push @{ $self->{fields} }, $elem;
}
sub iter {
my ($self) = @_;
return iarray($self->{fields});
}
1;
按以下方式使用它:
use strict;
use warnings;
use feature qw( say );
use My::Collection qw( );
my $collection = My::Collection->new();
$collection->add("Hi");
$collection->add("Hello");
$collection->add("Yeet");
my $iter = $collection->iter();
while ( my ($item) = $iter->next() ) {
say $item;
}
您还可以使用以下两个更神奇的选项之一:
while ( my ($item) = $iter->() )
while ( my ($item) = <$iter> )