Perl重载@ {},以便您可以为foreach()提供一个对象

问题描述 投票:7回答:3

有没有办法让一个类重载数组解引用器,这样我就可以向foreach提供该类的实例,并在每次迭代时运行自定义函数?

我试过了:

  • 重载@{}并将其分配给子。但是,在这种情况下,sub只执行一次,并且应该返回对要迭代的数组的引用。几乎但不完全是我想要的。
  • 重载<>。这将在每次读取尝试时执行指定的功能。究竟我想要它,但它适用于while循环而不是foreach

overloading.评论

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.屏幕

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...
perl operator-overloading
3个回答
5
投票

你在这里遇到的问题不是@{...}<...>之间的区别,而是foreachwhile之间的区别。

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())只会被调用一次,并且会返回一个值列表。


2
投票

我在Object::Iterate展示了一些技巧。给你的对象一些方法来提供下一个值并从那里开始。


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> )
© www.soinside.com 2019 - 2024. All rights reserved.