Как мне вызвать имя функции, которое хранится в хэше в Perl?

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

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 для поиска ссылки на код вместо принятия его соглашения об именах) нельзя также использовать для предикатов, так как Class :: MOP :: Attribute не имеет аналогичного get_predicate_method_ref метода доступа. :(


person Ether    schedule 02.12.2009    source источник
comment
Я не думаю, что это возможно из-за порядка синтаксического анализа Perl. Почему вы не хотите сначала скопировать $ hash {func} в скаляр?   -  person Mikael S    schedule 03.12.2009
comment
Нет особой причины, за исключением того, что она кажется ненужной, и это была интересная загадка, которая поставила меня в тупик. Я не поверил этому просто потому, что не знал ответа, которого нет. :) (tl; dr version: мне любопытно!)   -  person Ether    schedule 03.12.2009
comment
Эээ, мне кажется, что если вы используете Moose, то вы все равно делаете это неправильно. Одна из особенностей Moose - это мета-объектная модель, на которой он построен ... У меня есть ощущение, что есть метод, который вы можете вызвать для поиска фактического подзаголовка по имени строки, который затем можно было бы вызвать, вместо использования пустых строк . Хотя я не знаю это в голове ...   -  person Robert P    schedule 03.12.2009
comment
@RobertP: Я думаю, вы имеете в виду my $reader_ref = $this->meta->find_attribute_by_name($fieldname)->get_read_method_ref; (см. Class :: MOP :: Class и Class :: MOP :: Attribute), но все это позволяет избежать того, что читателю присвоено имя, отличное от имени атрибута.   -  person Ether    schedule 03.12.2009
comment
PS. завтра я опубликую актуальный код, с которым имел дело, чтобы мы могли больше его критиковать :) действительно может быть лучший способ сделать то, что я делал. Однако это в основном ортогонально моему любопытству по этому конкретному вопросу синтаксиса.   -  person Ether    schedule 03.12.2009
comment
Да, я так и думал. Для тех, кто смотрит на этот фрагмент, когда он у вас есть, вы можете просто сделать $this->$reader_ref(), и он будет намного чище, чем версия по имени. У PBP есть хорошее объяснение, почему использование подссылки - лучший способ сделать это, если кому-то интересно.   -  person Robert P    schedule 03.12.2009
comment
В качестве исторической сноски я должен сейчас отметить, что весь приведенный выше код был уничтожен с орбиты в ходе последующего рефакторинга. 1. безумие пытаться заново реализовать ORM, когда есть много хороших на выбор, и 2. в Moose лучше вызывать $attr->set_value($instance, $value), чем делать предположения о том, что вызывается аксессор.   -  person Ether    schedule 08.06.2010
comment
Я предполагаю, что вы имели в виду Moose :: Exporter в Edit 2.   -  person Christopher Bottoms    schedule 10.06.2010
comment
@molecules: да, спасибо!   -  person Ether    schedule 10.06.2010


Ответы (3)


Foo->${\$hash{func}};

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

my $method = $hash{func};
Foo->$method;
person runrig    schedule 02.12.2009
comment
Просто и очевидно; Я люблю это! - person Ether; 03.12.2009
comment
Я понимаю первый знак доллара. Но чего я не понимаю, так это обратной косой черты. - person innaM; 03.12.2009
comment
@Manni: $ {...} анализируется как скалярное разыменование, а не как устранение неоднозначности, потому что содержимое не является буквальной строкой. Обратная косая черта служит для создания ссылки на значение $ hash {func}. - person Michael Carman; 03.12.2009
comment
Комментарий Манни - отличный пример того, почему вы не должны использовать эту конструкцию в производственном коде. Это не так просто, как кажется на первый взгляд. Как это работает (и, соответственно, то, что он делает, когда вы вернетесь к нему через год) может быть непонятным даже для опытных программистов Perl. Намного лучше писать ясный код, чем писать умный код: my $method = $hash{func}; Foo->$method(). - person Michael Carman; 03.12.2009
comment
Я в основном согласен с Майклом, поэтому обновил свой ответ, чтобы отразить это. На самом деле я никогда не использовал более короткий способ, кроме как для того, чтобы убедиться, что он работает, но я использовал более длинный способ много раз. - person runrig; 03.12.2009
comment
Ой интересно. Задокументирован ли синтаксис метода Foo - ›$ в официальном документе? Я огляделась и, кажется, не нашла. - person solstice333; 27.09.2016

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

e.g.

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(@_) } );
person jsoverson    schedule 02.12.2009
comment
Да, потому что дополнительные имена соответствуют именам атрибутов, взятым из объектов Moose. - person Ether; 03.12.2009
comment
Я отредактировал свой вопрос, чтобы указать, что Foo может быть либо именем класса, либо благословенным объектом. - person Ether; 03.12.2009
comment
Вам не нужна дополнительная ссылка, потому что это нарушает наследование. - person brian d foy; 26.01.2011
comment
Итак, вместо вспомогательной ссылки используйте закрытие над именем метода, например my% hash = (func = ›foo); мой% method_hash = map {my $ meth = $ hash {$ _}; $ _ = ›Sub {Foo -› $ meth}} keys% hash; $ method_hash {func} - ›(); - person MkV; 10.02.2012

Вы пробовали использовать метод 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()'
person gpojd    schedule 03.12.2009
comment
Да, но опять же, это отвлечение от моего первоначального вопроса о том, как использовать хеш-значение в качестве имени метода (на который ответил runrig). can в этом случае является менее совершенным решением, чем то, которое я обрисовал в редактировании вопроса, поскольку оно делает предположения об именовании читателя Moose. - person Ether; 03.12.2009