如何在 Perl 中调用存储在哈希中的函数名称?

发布于 2024-08-13 05:42:03 字数 2146 浏览 10 评论 0原文

我确信这在文档中的某个地方有所涉及,但我一直找不到它...我正在寻找语法糖,它可以调用名称存储在哈希中的类上的方法(而不是简单的标量):

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::Attribute 没有类似的 get_predicate_method_ref 访问器。 :(

I'm sure this is covered in the documentation somewhere but I have been unable to find it... I'm looking for the syntactic sugar that will make it possible to call a method on a class whose name is stored in a hash (as opposed to a simple scalar):

use strict; use warnings;

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

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

Foo->$hash{func};

If I copy $hash{func} into a scalar variable first, then I can call Foo->$func just fine... but what is missing to enable Foo->$hash{func} to work?

(EDIT: I don't mean to do anything special by calling a method on class Foo -- this could just as easily be a blessed object (and in my actual code it is); it was just easier to write up a self-contained example using a class method.)

EDIT 2: Just for completeness re the comments below, this is what I'm actually doing (this is in a library of Moose attribute sugar, created with 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);
        },
    );
}

I've replaced the marked line above with this:

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

PS. I've just noticed that this same technique (using the Moose meta class to look up the coderef rather than assuming its naming convention) cannot also be used for predicates, as Class::MOP::Attribute does not have a similar get_predicate_method_ref accessor. :(

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

黎夕旧梦 2024-08-20 05:42:03
Foo->${\$hash{func}};

但为了清楚起见,我可能仍将其写为:

my $method = $hash{func};
Foo->$method;
Foo->${\$hash{func}};

But for clarity, I'd probably still write it as:

my $method = $hash{func};
Foo->$method;
寒江雪… 2024-08-20 05:42:03

您存储子例程名称而不是代码引用是否有原因?

例如,

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(@_) } );

Is there a reason you are storing subroutine names instead of the references to code?

e.g.

use strict; use warnings;

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

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

$hash{func}->();

You won't be passing the class name, but if that's important to you, you can use something like

my %hash = ( func => sub { return Foo->foo(@_) } );
山人契 2024-08-20 05:42:03

您是否尝试过UNIVERSALcan方法?你应该能够实现这样的东西:

## 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()'

Have you tried UNIVERSAL's can method? You should be able to implement something like this:

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

I made a useless, one-line example to demonstrate:

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()'
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文