Как мне вызвать имя функции, которая хранится в хэше в Perl? - PullRequest
8 голосов
/ 03 декабря 2009

Я уверен, что это где-то описано в документации, но я не смог его найти ... Я ищу синтаксический сахар, который позволит вызвать метод для класса, имя которого хранится в хеш (в отличие от простого скаляра):

use strict; use warnings;

package Foo;
sub foo { print "in foo()\n" }

package main;
my %hash = (func => 'foo');

Foo->$hash{func};

Если я сначала скопирую $hash{func} в скалярную переменную, тогда я могу вызвать Foo->$func просто отлично ... но чего не хватает, чтобы Foo->$hash{func} заработал?

(РЕДАКТИРОВАТЬ: я не имею в виду делать что-то особенное, вызывая метод в классе Foo - это может быть просто благословенный объект (и в моем реальном коде это так); это было просто легче написать автономный пример, используя метод класса.)

РЕДАКТИРОВАТЬ 2: Просто для полноты из комментариев ниже, это то, что я на самом деле делаю (это в библиотеке сахара атрибута Moose, созданной с помощью Moose :: Exporter ):

# adds an accessor to a sibling module
sub foreignTable
{
    my ($meta, $table, %args) = @_;

    my $class = 'MyApp::Dir1::Dir2::' . $table;
    my $dbAccessor = lcfirst $table;

    eval "require $class" or do { die "Can't load $class: $@" };

    $meta->add_attribute(
        $table,
        is => 'ro',
        isa => $class,
        init_arg => undef,  # don't allow in constructor
        lazy => 1,
        predicate => 'has_' . $table,
        default => sub {
            my $this = shift;
            $this->debug("in builder for $class");

            ### here's the line that uses a hash value as the method name
            my @args = ($args{primaryKey} => $this->${\$args{primaryKey}});
            push @args, ( _dbObject => $this->_dbObject->$dbAccessor )
                if $args{fkRelationshipExists};

            $this->debug("passing these values to $class -> new: @args");
            $class->new(@args);
        },
    );
}

Я заменил отмеченную строку выше на это:

        my $pk_accessor = $this->meta->find_attribute_by_name($args{primaryKey})->get_read_method_ref;
        my @args = ($args{primaryKey} => $this->$pk_accessor);

PS. Я только что заметил, что этот же метод (использование мета-класса Moose для поиска coderef вместо предположения о соглашении об именах) не может также использоваться для предикатов, так как Class :: MOP :: Атрибут не имеет аналогичного get_predicate_method_ref метода доступа. (

Ответы [ 3 ]

14 голосов
/ 03 декабря 2009
Foo->${\$hash{func}};

Но для ясности, я бы, наверное, все равно написал бы это:

my $method = $hash{func};
Foo->$method;
2 голосов
/ 03 декабря 2009

Есть ли причина, по которой вы храните имена подпрограмм вместо ссылок на код?

, например

use strict; use warnings;

package Foo;
sub foo { print "in foo()\n" }

package main;
my %hash = (func => \&Foo::foo);

$hash{func}->();

Вы не будете передавать имя класса, но если это важно для вас, вы можете использовать что-то вроде

my %hash = ( func => sub { return Foo->foo(@_) } );
1 голос
/ 03 декабря 2009

Вы пробовали UNIVERSAL can метод? Вы должны быть в состоянии реализовать что-то вроде этого:

## untested
if ( my $code = $object->can( $hash{func} ) ) {
    $object->$code();
}

Я сделал бесполезный однострочный пример для демонстрации:

perl -MData::Dumper -le 'my %h = ( f => "Dump" ); my $o = Data::Dumper->new( [qw/1 2 3/] ); my $ref = $o->can( $h{f} ); print $o->$ref()'
...