Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I use an index in an array reference as a method reference in Perl?

Tags:

reference

perl

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.

like image 481
Robert P Avatar asked May 10 '10 15:05

Robert P


People also ask

How do I index an array in Perl?

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.

Can you access an array element by referring to the index?

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.

How will you create a reference to an array variable in Perl?

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.

How do you reference an element in an array?

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).


1 Answers

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.


Update

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
like image 165
Greg Bacon Avatar answered Nov 07 '22 03:11

Greg Bacon