如何解析传递给定义为在 Perl 中接受 2 个参数的子例程的一个参数

How do I resolve one argument passed to a subroutine defined to accept 2 parameters in Perl

我正在创建一个新对象如下:

my $new_obj = new P_module({key => 'abc'});

P_module的构造函数定义如下:

sub new {
    my ($pkg, $input) = @_;
    my $obj = {};
    bless ($obj, ref($pkg)||$pkg);
    $obj->{key} = $input->{key};
 }

据我所知,由于散列作为参数传递给 new,因此它将表示为 2 元素数组。所以,pkg 应该是 keyinput 应该是 abc。那么,obj 是如何掌握一把钥匙 key 的,$input->{key} 又是什么意思呢?

周围的大括号创建一个哈希 reference 或 hashref,这是一个单一的标量。参数值为

  • $pkg — 包含包的名称,可能是 "P_module"
  • $input — 引用提供的散列

代码 $input->{key} 访问与键 "key" 关联的值,该键由调用者提供

my $new_obj = new P_module({key => 'abc'});

这样写比较好

my $new_obj = P_module->new({key => 'abc'});

您可以添加另一个密钥,如

my $new_obj = P_module->new({key => 'abc', other => 'unused'});

"key" 关联的值被复制到对象的状态

$obj->{key} = $input->{key};

请注意,Perl 子例程 return 最后一个表达式的值,因此您的代码有错误。您希望 P_module::new 的最后一行是

bless ($obj, ref($pkg)||$pkg);

因为这将 return 可用于调用其他方法的受祝福对象,例如

my $new_obj = new P_module({key => 'abc'});
print $new_obj->other_method_that_you_must_define(), "\n";

首先,

my $new_obj = new P_module({ key => 'abc' });

最好写成

my $new_obj = P_module->new({ key => 'abc' });

这是

的缩写
my %anon = ( key => 'abc' );
my $new_obj = P_module->new(\%anon);

进行方法调用时,调用者(-> 的剩余部分)作为第一个参数传递。这意味着 $pkg 是字符串 P_module,而 $input{ key => 'abc' }.

返回的引用

由于 $input 是对散列的引用,$input->{key} 从引用的散列中获取具有键 key 的元素的值。


我会怎么写:

sub new {
    my ($class, %args) = @_;
    my $self = bless({}, $class);      # If base class.
    #my $self = $class->SUPER::new();  # If inheriting.
    $self->{key} = $args{$key};
    return $self;
 }

 my $obj = P_module->new( key => 'abc' );

调用者不需要散列,也没有理由支持$existing_obj->new。它还使用更多标准名称 $class$self.

你有这样的原因吗:

bless ($obj, ref($pkg)||$pkg);

而不是这个?

bless ($obj, $pkg);

如果不是,那么我假设您是从某处的其他代码中剪切并粘贴的。你可能想要的是后者。

ref($pkg)||$pkg 允许您这样做:

my $new_object = $existing_obj->new;

而不是

my $new_object = new Classname;

既然你可能不需要这样做,那就坚持使用

bless ($obj, $pkg);

当您调用方法时,对象本身会在幕后传递;或者,如果 sub 使用 bless 则它是构造函数,而是传递 class 名称。因此,任何方法都会接收该对象或 class 名称作为其第一个参数。其余的是显式传递给方法的参数。

你传递一个散列引用{...},什么是标量。因此,它被分配给 $input,假设 "quitely" 通过 class 名称被分配给 $pkg

该接口的构造函数,带有哈希引用,通常简单地写为

sub new {
    my ($class, $input) = @_;
    my $self = { %$input };
    return bless $self, $class;
}

其中 return 可以省略,因为最后评估语句的 return 是 returned。最后两行可以写成return bless { %$input }, $class;,一个(匿名的)hashref被初始化,blessed到它的包$class中(所以它是一个对象),然后returned.

请注意,您不必传递哈希引用;一个简单的散列使得界面更清晰。如果可能还有其他参数,则需要使用参考, 使解析所有这些成为可能。

但是,您通常希望处理参数。首先,您 必须 检查它们是否符合您的 class 支持的内容,而不是盲目地将用户传递的键分配给属性。如果您提供默认值,然后计算默认值,如果需要检查值等。

这种数据初始化有时会被拆分成一个单独的子程序。例如

sub new {
    my ($class, $input) = @_;
    my $self = {};
    bless $self, $class;       # $self is an object now
    $self->_init($input);      # on which methods can be called
    return $self;
}

sub _init {
    my ($self, $args) = @_;
    # Check arguments, work out defaults (etc), 
    # then assign to self as appropriate
    %$self = %$args;
}

其中代码通过引用写入 "back",以便更新 new 中的 $self

这应该与普通方法调用一起使用

my $new_obj = P_module->new({key => 'abc', other => '...'});

使用间接表示法 new P_module。构造函数,主要称为 new(只是根据习惯——你可以随意称呼它),是一个普通的子特殊,只是因为它 bless 是一个引用(通常是一个散列引用),因此被传递class 名称而不是对象。因此它 "knows" 它的包并且是一个对象。

文学:perlmod (a class is a package), tutorial perlootut, and reference perlobj.

一旦您熟悉了 Perl 的内置面向对象功能(我建议您这样做),有许多模块可以使所有这些(以及更多)操作变得更容易。

首先推荐的是非常现代的Moose and its light-weight counterpart Moo