Similar to this question about iterating over subroutine references, and as a result of answering this question about a OO dispatch table, I was wondering how to call a method reference inside a reference, without removing it first, or if it was even possible.
For example:
package Class::Foo;
use 5.012; #Yay autostrict!
use warnings;
# a basic constructor for illustration purposes....
sub new {
my $class = shift;
return bless {@_}, $class;
}
# some subroutines for flavor...
sub sub1 { say 'in sub 1'; return shift->{a} }
sub sub2 { say 'in sub 2'; return shift->{b} }
sub sub3 { say 'in sub 3'; return shift->{c} }
# and a way to dynamically load the tests we're running...
sub sublist {
my $self = shift;
return [
$self->can('sub1'),
$self->can('sub3'),
$self->can('sub2'),
];
}
package main;
sub get_index { ... } # details of how we get the index not important
my $instance = Class::Foo->new(a => 1, b => 2, c => 3);
my $subs = $instance->sublist();
my $index = get_index();
# <-- HERE
So, at HERE, we could do:
my $ref = $subs->[$index];
$instance->$ref();
but how would we do this, without removing the reference first?
Edit:
Changed code example so people don't get hung up on implementation details (sigh, tried my best). The important difference between this and the first link I gave was that the function should be invoked as a method, not as a straight subroutine.
Edit 2:
See the discussion in the linked comment about the technical details, and why the longer way (storing the subref to a variable, then calling it) is probably preferable.
Perl access array elements Array elements are access by their indexes. The first index has value 0. The last index is $#vals . Array elements can be accessed from the end by using negative indexes.
You can access an array element by referring to its index number. The indexes in NumPy arrays start with 0, meaning that the first element has index 0, and the second has index 1 etc.
A reference to an anonymous array can be created using square brackets: $arrayref = [1, 2, ['a', 'b', 'c']]; Here we've created a reference to an anonymous array of three elements whose final element is itself a reference to another anonymous array of three elements.
The entire array can be referenced using just the array name, with no index. This is useful for assigning the same value to every element or clearing all the values in the array. The syntax is dim array([lbound to] ubound).
As written, you can get away with
$tests->[$index]();
because the methods in your question aren't using $self
.
You could pass $instance
explicitly, but that's clunky. Better would be to simulate delegates with closures:
sub sublist {
my $self = shift;
my $sublist;
for (qw/ sub1 sub3 sub2 /) {
my $meth = $_;
push @$sublist => sub { $self->$meth() };
}
return $sublist;
}
If you prefer to be concise, use
sub sublist {
my $self = shift;
return [ map { my $meth = $_; sub { $self->$meth() } }
qw/ sub1 sub3 sub2 / ];
}
Calling one at random is still
$tests->[$index]();
but now the methods get invocants.
Grabbing subrefs via can
appears to be unnecessary complexity. If a runtime-determined list of names of methods to call will do, then you can simplify your code greatly:
sub sublist {
my $self = shift;
return [ qw/ sub1 sub3 sub2 / ];
}
Below, we call them all for testing purposes, but you can also see how to call only one:
foreach my $method (@$subs) {
my $x = $instance->$method();
say "$method returned $x";
}
Output:
in sub 1 sub1 returned 1 in sub 3 sub3 returned 3 in sub 2 sub2 returned 2
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