Is there a way I can make a class overload the array dereferencer in such a way that I can supply an instance of that class to foreach
and have a custom function run at every iteration?
I tried:
Overloading @{}
and assigning it to a sub. However, in this case, the sub is only executed once and is supposed to return a reference to the array you want to iterate over. Almost but not quite what I wanted.
Overloading <>
. This executes the assigned function with every read attempt. Exactly how I want it, however it works with a while
loop and not with 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;
output:
$ 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...
The problem that you're having here is not the difference between @{...}
and <...>
but the difference between foreach
and while
.
A while
loop acts a bit like an iterator in as much as its execution looks like this:
while (you can run a piece of code and get back a value) {
do something
}
So each time around the loop, it executes the piece of code in the while
condition and expects to get a single value back. The code in the while
condition is run in scalar context.
A foreach
loop, on the other hand, only executes its code once and expects to get a list of values back. The code between parentheses at the start of the loop is executed in list context.
This is why you read a large file a line at a time using:
while (<$file_handle>) {
...
}
This only reads a single record from the file at a time. If you used a foreach
loop instead like this:
foreach (<$file_handle>) {
...
}
then you would get all of the records back from the filehandle at once - which, obviously, takes far more memory.
Dereferencing an array reference works like that too. You get all of the values back at the same time. The overridden method (next()
) will only be called once and will be expected to return a list of values.
I show some techniques in Object::Iterate. Give your object some methods to supply the next value and go from there.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With