我可以在 Perl 中传递对象方法的代码引用吗?
Can I pass the code reference of an object's method in Perl?
在处理各种获取和设置参数的网络处理程序中,我大量使用了闭包。
我有一个子例程,它接收一个闭包并使用作为参数传递给 return 的闭包构建另一个闭包(听起来很复杂,但这就是 为什么 我想要这样)。
现在我有一个情况,我必须传递两个非常相似的闭包,每个都使用相同的对象方法,但具有不同的参数(对象方法检查传递的参数数量)。
我的想法不是传递两个(或更多)类似的闭包,而是传递一个引用$meth_ref
给对象的方法(对象也传递给函数returning闭包),这样函数就可以使用代码参考来传递不同的参数。
不幸的是我没有找到这样做的语法。
代码草图:
sub closure_maker($$)
{
my ($obj, $meth_ref) = @_;
return sub (...) {
$meth_ref->($obj);
...
$meth_ref->($obj, ...);
};
}
my @handlers = (closure_maker($obj1, ???), closure_maker($obj2, ???));
希望你明白了。
您可以使用 \&Classname::method
获得对方法的引用,然后可以将其用于 class 的给定对象。例如:
#!/usr/bin/env perl
use strict;
use warnings;
use feature qw/say/;
package Example {
sub new {
my ($class, $name) = @_;
return bless { name => $name }, $class;
}
sub method1 {
my $self = shift;
say "Method 1 of $self->{name}: @_";
}
};
sub make_closure($$) {
my ($obj, $method) = @_;
return sub { $obj->$method(@_); }
}
my $obj1 = Example->new("Bob");
my $obj2 = Example->new("Cindy");
my $closure1 = make_closure($obj1, \&Example::method1);
my $closure2 = make_closure($obj2, \&Example::method1);
$closure1->(qw/1 2/);
$closure2->(qw/A B C/);
产出
Method 1 of Bob: 1 2
Method 1 of Cindy: A B C
如果您有疑虑,您可能希望对这些方法添加类型检查,以确保它们不会被错误的对象意外调用 class。在 perl 5.32 中添加的 isa
运算符使这变得简单:
# In package Example
use Carp;
sub method1 {
use experimental qw/isa/;
my $self = shift;
croak "Invalid object; should be an Example" unless $self isa Example;
say "Method 1 of $self->{name}: @_";
}
旧版本可以使用built-in isa
方法:
# In package Example
use Carp;
use Scalar::Util qw/blessed/;
sub method1 {
my $self = shift;
croak "Invalid object; should be an Example"
unless defined blessed $self && $self->isa("Example");
say "Method 1 of $self->{name}: @_";
}
我找到了一个非常丑陋但有效的解决方案。
示例会话来自真实代码:
### this is the sample method:
DB<5> x MessageLogFormat::log_appname($log_format)
0 ''
DB<11> $xxxx = \&{*{MessageLogFormat::log_appname}}
DB<12> x ref $xxxx
0 'CODE'
DB<13> x $xxxx->($log_format)
0 ''
DB<14> x $xxxx->($log_format, 1)
0 1
### NAME() just outputs the name of the class
DB<17> $n = $log_format->NAME()
DB<19> $xxx = \&{*{${n}.'::log_appname'}}
### the method is actually a closure (provided by class `Class`) by itself
DB<20> x $xxx
0 CODE(0x1af8960)
-> &Class::__ANON__[lib/Class.pm:85] in lib/Class.pm:78-85
DB<21> x $xxx->($log_format)
0 1
DB<22> x $xxx->($log_format, 0)
0 ''
DB<23> x $xxx->($log_format)
0 ''
### So that worked; try a non-closure method, too:
DB<24> $xxx = \&{*{${n}.'::new'}}
DB<25> x $xxx
0 CODE(0x1afcb88)
-> &MessageLogFormat::new in lib/MessageLogFormat.pm:85-91
在草拟的解决方案中,我不必知道(并明确写下)每个正在使用的对象的 class。
使用$obj->$method_name()
.
sub closure_maker($$)
{
my ($obj, $method_name) = @_;
return sub {
$obj->$method_name();
...
$obj->$method_name(...);
};
}
my @handlers = map { closure_maker($_, "method_name") } $obj1, $obj2;
您可以使用 $obj->can
获取方法的引用。
sub closure_maker($$)
{
my ($obj, $method_name) = @_;
my $method_ref = $obj->can($method_name);
return sub {
$obj->$method_ref();
...
$obj->$method_ref(...);
};
}
在处理各种获取和设置参数的网络处理程序中,我大量使用了闭包。 我有一个子例程,它接收一个闭包并使用作为参数传递给 return 的闭包构建另一个闭包(听起来很复杂,但这就是 为什么 我想要这样)。 现在我有一个情况,我必须传递两个非常相似的闭包,每个都使用相同的对象方法,但具有不同的参数(对象方法检查传递的参数数量)。
我的想法不是传递两个(或更多)类似的闭包,而是传递一个引用$meth_ref
给对象的方法(对象也传递给函数returning闭包),这样函数就可以使用代码参考来传递不同的参数。
不幸的是我没有找到这样做的语法。
代码草图:
sub closure_maker($$)
{
my ($obj, $meth_ref) = @_;
return sub (...) {
$meth_ref->($obj);
...
$meth_ref->($obj, ...);
};
}
my @handlers = (closure_maker($obj1, ???), closure_maker($obj2, ???));
希望你明白了。
您可以使用 \&Classname::method
获得对方法的引用,然后可以将其用于 class 的给定对象。例如:
#!/usr/bin/env perl
use strict;
use warnings;
use feature qw/say/;
package Example {
sub new {
my ($class, $name) = @_;
return bless { name => $name }, $class;
}
sub method1 {
my $self = shift;
say "Method 1 of $self->{name}: @_";
}
};
sub make_closure($$) {
my ($obj, $method) = @_;
return sub { $obj->$method(@_); }
}
my $obj1 = Example->new("Bob");
my $obj2 = Example->new("Cindy");
my $closure1 = make_closure($obj1, \&Example::method1);
my $closure2 = make_closure($obj2, \&Example::method1);
$closure1->(qw/1 2/);
$closure2->(qw/A B C/);
产出
Method 1 of Bob: 1 2
Method 1 of Cindy: A B C
如果您有疑虑,您可能希望对这些方法添加类型检查,以确保它们不会被错误的对象意外调用 class。在 perl 5.32 中添加的 isa
运算符使这变得简单:
# In package Example
use Carp;
sub method1 {
use experimental qw/isa/;
my $self = shift;
croak "Invalid object; should be an Example" unless $self isa Example;
say "Method 1 of $self->{name}: @_";
}
旧版本可以使用built-in isa
方法:
# In package Example
use Carp;
use Scalar::Util qw/blessed/;
sub method1 {
my $self = shift;
croak "Invalid object; should be an Example"
unless defined blessed $self && $self->isa("Example");
say "Method 1 of $self->{name}: @_";
}
我找到了一个非常丑陋但有效的解决方案。 示例会话来自真实代码:
### this is the sample method:
DB<5> x MessageLogFormat::log_appname($log_format)
0 ''
DB<11> $xxxx = \&{*{MessageLogFormat::log_appname}}
DB<12> x ref $xxxx
0 'CODE'
DB<13> x $xxxx->($log_format)
0 ''
DB<14> x $xxxx->($log_format, 1)
0 1
### NAME() just outputs the name of the class
DB<17> $n = $log_format->NAME()
DB<19> $xxx = \&{*{${n}.'::log_appname'}}
### the method is actually a closure (provided by class `Class`) by itself
DB<20> x $xxx
0 CODE(0x1af8960)
-> &Class::__ANON__[lib/Class.pm:85] in lib/Class.pm:78-85
DB<21> x $xxx->($log_format)
0 1
DB<22> x $xxx->($log_format, 0)
0 ''
DB<23> x $xxx->($log_format)
0 ''
### So that worked; try a non-closure method, too:
DB<24> $xxx = \&{*{${n}.'::new'}}
DB<25> x $xxx
0 CODE(0x1afcb88)
-> &MessageLogFormat::new in lib/MessageLogFormat.pm:85-91
在草拟的解决方案中,我不必知道(并明确写下)每个正在使用的对象的 class。
使用$obj->$method_name()
.
sub closure_maker($$)
{
my ($obj, $method_name) = @_;
return sub {
$obj->$method_name();
...
$obj->$method_name(...);
};
}
my @handlers = map { closure_maker($_, "method_name") } $obj1, $obj2;
您可以使用 $obj->can
获取方法的引用。
sub closure_maker($$)
{
my ($obj, $method_name) = @_;
my $method_ref = $obj->can($method_name);
return sub {
$obj->$method_ref();
...
$obj->$method_ref(...);
};
}