面向对象的 Perl 构造函数语法和命名参数

发布于 2024-08-10 08:51:18 字数 1636 浏览 5 评论 0原文

我对 Perl 构造函数中发生的事情有点困惑。我找到了这两个示例 perldoc perlbot

package Foo;

#In Perl, the constructor is just a subroutine called new.
sub new {
  #I don't get what this line does at all, but I always see it. Do I need it?
  my $type = shift;

  #I'm turning the array of inputs into a hash, called parameters.
  my %params = @_;

  #I'm making a new hash called $self to store my instance variables?
  my $self = {};

  #I'm adding two values to the instance variables called "High" and "Low".
  #But I'm not sure how $params{'High'} has any meaning, since it was an
  #array, and I turned it into a hash.
  $self->{'High'} = $params{'High'};
  $self->{'Low'} = $params{'Low'};

  #Even though I read the page on [bless][2], I still don't get what it does.
  bless $self, $type;
}

另一个例子是:

package Bar;

sub new {
  my $type = shift;

  #I still don't see how I can just turn an array into a hash and expect things
  #to work out for me.
  my %params = @_;
  my $self = [];

  #Exactly where did params{'Left'} and params{'Right'} come from?
  $self->[0] = $params{'Left'};
  $self->[1] = $params{'Right'};

  #and again with the bless.
  bless $self, $type;
}

这是使用这些对象的脚本:

package main;

$a = Foo->new( 'High' => 42, 'Low' => 11 );
print "High=$a->{'High'}\n";
print "Low=$a->{'Low'}\n";

$b = Bar->new( 'Left' => 78, 'Right' => 40 );
print "Left=$b->[0]\n";
print "Right=$b->[1]\n";

我已将我一直遇到的问题/困惑作为注释注入到代码中。

I'm a little confused about what is going on in Perl constructors. I found these two examples perldoc perlbot.

package Foo;

#In Perl, the constructor is just a subroutine called new.
sub new {
  #I don't get what this line does at all, but I always see it. Do I need it?
  my $type = shift;

  #I'm turning the array of inputs into a hash, called parameters.
  my %params = @_;

  #I'm making a new hash called $self to store my instance variables?
  my $self = {};

  #I'm adding two values to the instance variables called "High" and "Low".
  #But I'm not sure how $params{'High'} has any meaning, since it was an
  #array, and I turned it into a hash.
  $self->{'High'} = $params{'High'};
  $self->{'Low'} = $params{'Low'};

  #Even though I read the page on [bless][2], I still don't get what it does.
  bless $self, $type;
}

And another example is:

package Bar;

sub new {
  my $type = shift;

  #I still don't see how I can just turn an array into a hash and expect things
  #to work out for me.
  my %params = @_;
  my $self = [];

  #Exactly where did params{'Left'} and params{'Right'} come from?
  $self->[0] = $params{'Left'};
  $self->[1] = $params{'Right'};

  #and again with the bless.
  bless $self, $type;
}

And here is the script that uses these objects:

package main;

$a = Foo->new( 'High' => 42, 'Low' => 11 );
print "High=$a->{'High'}\n";
print "Low=$a->{'Low'}\n";

$b = Bar->new( 'Left' => 78, 'Right' => 40 );
print "Left=$b->[0]\n";
print "Right=$b->[1]\n";

I've injected the questions/confusion that I've been having into the code as comments.

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

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

发布评论

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

评论(7

欲拥i 2024-08-17 08:51:18

要回答您问题的主要要点,因为哈希可以初始化为 key => 的列表。值 对,您可以将这样的列表发送到函数,然后将 @_ 分配给哈希。这是在 Perl 中执行命名参数的标准方法。

例如,

sub foo { 
    my %stuff = @_;
    ...
}

foo( beer => 'good', vodka => 'great' );

这将导致子例程 foo 中的 %stuff 具有包含两个键的哈希:beervodka ,以及相应的值。

现在,在 OO Perl 中,还有一些额外的问题。每当您使用箭头 (->) 运算符调用方法时,箭头左侧的任何内容都会粘贴到 @_ 数组的开头。

因此,如果您说 Foo->new( 1, 2, 3 );

然后在构造函数中,@_ 将如下所示:( 'Foo', 1, 2, 3 )

因此,我们使用 shift,它不带参数隐式地对 @_ 进行操作,从 @_ 中获取第一项,并将其分配给$类型。之后,@_只剩下我们的名称/值对,为了方便起见,我们可以将其直接分配给哈希值。

然后我们使用该 $type 值来表示 blessbless 所做的就是获取一个引用(在第一个示例中是哈希引用)并说“此引用与特定包关联”。阿拉卡扎姆,你有一个目标。

请记住,$type 包含字符串“Foo”,这是我们包的名称。如果您没有bless指定第二个参数,它将使用当前包的名称,这在本例中也可以工作,但不会< /em> 适用于继承的构造函数。

To answer the main thrust of your question, since a hash can be initialized as a list of key => value pairs, you can send such a list to a function and then assign @_ to a hash. This is the standard way of doing named parameters in Perl.

For example,

sub foo { 
    my %stuff = @_;
    ...
}

foo( beer => 'good', vodka => 'great' );

This will result in %stuff in subroutine foo having a hash with two keys, beer and vodka, and the corresponding values.

Now, in OO Perl, there's some additional wrinkles. Whenever you use the arrow (->) operator to call a method, whatever was on the left side of the arrow is stuck onto the beginning of the @_ array.

So if you say Foo->new( 1, 2, 3 );

Then inside your constructor, @_ will look like this: ( 'Foo', 1, 2, 3 ).

So we use shift, which without an argument operates on @_ implicitly, to get that first item out of @_, and assign it to $type. After that, @_ has just our name/value pairs left, and we can assign it directly to a hash for convenience.

We then use that $type value for bless. All bless does is take a reference (in your first example a hash ref) and say "this reference is associated with a particular package." Alakazzam, you have an object.

Remember that $type contains the string 'Foo', which is the name of our package. If you don't specify a second argument to bless, it will use the name of the current package, which will also work in this example but will not work for inherited constructors.

大海や 2024-08-17 08:51:18

.1.在 Perl 中,构造函数只是一个名为 new 的子例程。

是的,按照惯例 new 是一个构造函数。它也可以执行初始化或不执行初始化。 new 应该在成功时返回一个对象,或者在发生阻止对象创建的错误时抛出异常 (die/croak)。

您可以将构造函数命名为任何您喜欢的名称,拥有任意数量的构造函数,甚至可以将 bless 对象构建到您想要的任何名称空间中(这并不是一个好主意)。

.2.我根本不明白 my $type = shift; 做了什么,但我总是看到它。我需要它吗?

不带参数的 shift@_ 头部取出一个参数,并将其分配给 $type-> 运算符将调用者(左侧)作为第一个参数传递给子例程。所以这一行从参数列表中获取类名。而且,是的,你确实需要它。

.3.输入数组如何成为 %params 哈希? my %params = @_;

对哈希的赋值是在列表上下文中完成的,列表项对被分组为键/值对。因此 %foo = 1, 2, 3, 4; 创建一个哈希,使得 $foo{1} == 2$foo{3} = = 4 。这通常是为了为子例程创建命名参数。如果子传递奇数个参数,则在启用警告的情况下将生成警告。

.4. 'my $self = {};` 是做什么的?

此行创建一个匿名哈希引用并将其分配给词法作用域变量 $self。哈希引用将存储对象的数据。通常,散列中的键与对象属性具有一对一的映射。因此,如果 Foo 类具有属性“size”和“color”,如果您检查 Foo 对象的内容,您将看到类似 $foo = { size =>; 'm',颜色=> '黑色'};

.5.给定 $self->{'High'} = $params{'High'}; $params{'High'} 来自哪里?

此代码依赖于传递给 new 的参数。如果像 Foo->new( High => 46 ) 那样调用 new,那么根据问题 3 创建的哈希将具有键 的值高(46)。在这种情况下,它相当于说 $self->{High} = 46。但如果像 Foo->new() 那样调用该方法,则没有可用的值,并且我们有 $self->{High} = undef

.6. bless 有什么作用?

bless 获取引用并与特定包关联,以便您可以使用它来进行方法调用。通过一个参数,引用与当前包相关联。对于两个参数,第二个参数指定与引用关联的包。最好始终使用两个参数形式,以便您的构造函数可以由子类继承并仍然正常运行。

最后,我将重写基于散列的对象访问器,就像我使用经典的 OO Perl 编写它一样。

package Foo;

use strict;
use warnings;
use Carp qw(croak);

sub new {
    my $class = shift;

    croak "Illegal parameter list has odd number of values" 
        if @_ % 2;

    my %params = @_;

    my $self = {};
    bless $self, $class;

    # This could be abstracted out into a method call if you 
    # expect to need to override this check.
    for my $required (qw{ name rank serial_number  });
        croak "Required parameter '$required' not passed to '$class' constructor"
            unless exists $params{$required};  
    }

    # initialize all attributes by passing arguments to accessor methods.
    for my $attrib ( keys %params ) {

        croak "Invalid parameter '$attrib' passed to '$class' constructor"
            unless $self->can( $attrib );

        $self->$attrib( $params{$attrib} );
    }

    return $self;
}

.1. In Perl, the constructor is just a subroutine called new.

Yes, by convention new is a constructor. It may also perform initialization or not. new should return an object on success or throw an exception (die/croak) if an error has occurred that prevents object creation.

You can name your constructor anything you like, have as many constructors as you like, and even build bless objects into any name space you desire (not that this is a good idea).

.2. I don't get what my $type = shift; does at all, but I always see it. Do I need it?

shift with no arguments takes an argument off the head of @_ and assigns it to $type. The -> operator passes the invocant (left hand side) as the first argument to the subroutine. So this line gets the class name from the argument list. And, yes, you do need it.

.3. How does an array of inputs become the %params hash? my %params = @_;

Assignment into a hash is done in list context, with pairs of list items being grouped into as key/value pairs. So %foo = 1, 2, 3, 4;, creates a hash such that $foo{1} == 2 and $foo{3} == 4. This is typically done to create named parameters for a subroutine. If the sub is passed an odd number of arguments, an warning will be generated if warnings are enabled.

.4. What does 'my $self = {};` do?

This line creates an anonymous hash reference and assigns it to the lexically scoped variable $self. The hash reference will store the data for the object. Typically, the keys in the hash have a one-to-one mapping to the object attributes. So if class Foo has attributes 'size' and 'color', if you inspect the contents of a Foo object, you will see something like $foo = { size => 'm', color => 'black' };.

.5. Given $self->{'High'} = $params{'High'}; where does $params{'High'} come from?

This code relies on the arguments passed to new. If new was called like Foo->new( High => 46 ), then the hash created as per question 3 will have a value for the key High (46). In this case it is equivalent to saying $self->{High} = 46. But if the method is called like Foo->new() then no value will be available, and we have $self->{High} = undef.

.6. What does bless do?

bless takes a reference and associates with a particular package, so that you can use it to make method calls. With one argument, the reference is assoicated with the current package. With two arguments, the second argument specifies the package to associate the reference with. It is best to always use the two argument form, so that your constructors can be inherited by a sub class and still function properly.

Finally, I'll rewrite your hash based object accessor as I would write it using classical OO Perl.

package Foo;

use strict;
use warnings;
use Carp qw(croak);

sub new {
    my $class = shift;

    croak "Illegal parameter list has odd number of values" 
        if @_ % 2;

    my %params = @_;

    my $self = {};
    bless $self, $class;

    # This could be abstracted out into a method call if you 
    # expect to need to override this check.
    for my $required (qw{ name rank serial_number  });
        croak "Required parameter '$required' not passed to '$class' constructor"
            unless exists $params{$required};  
    }

    # initialize all attributes by passing arguments to accessor methods.
    for my $attrib ( keys %params ) {

        croak "Invalid parameter '$attrib' passed to '$class' constructor"
            unless $self->can( $attrib );

        $self->$attrib( $params{$attrib} );
    }

    return $self;
}
似最初 2024-08-17 08:51:18

您的问题与 OO Perl 无关。您对数据结构感到困惑。

可以使用列表或数组来初始化哈希:

my @x = ('High' => 42, 'Low' => 11);
my %h = @x;

use Data::Dumper;
print Dumper \%h;
$VAR1 = {
          'Low' => 11,
          'High' => 42
        };

当您在 blessed 引用上调用方法时,该引用将被添加到该方法接收的参数列表中:

#!/usr/bin/perl

package My::Mod;

use strict;
use warnings;

use Data::Dumper;
$Data::Dumper::Indent = 0;

sub new { bless [] => shift }

sub frobnicate { Dumper(\@_) }

package main;

use strict;
use warnings;

my $x = My::Mod->new;

# invoke instance method
print $x->frobnicate('High' => 42, 'Low' => 11);

# invoke class method
print My::Mod->frobnicate('High' => 42, 'Low' => 11);

# call sub frobnicate in package My::Mod
print My::Mod::frobnicate('High' => 42, 'Low' => 11);

输出:

$VAR1 = [bless( [], 'My::Mod' ),'High',42,'Low',11];
$VAR1 = ['My::Mod','High',42,'Low',11];
$VAR1 = ['High',42,'Low',11];

Your question is not about OO Perl. You are confused about data structures.

A hash can be initialized using a list or array:

my @x = ('High' => 42, 'Low' => 11);
my %h = @x;

use Data::Dumper;
print Dumper \%h;
$VAR1 = {
          'Low' => 11,
          'High' => 42
        };

When you invoke a method on a blessed reference, the reference is prepended to the argument list the method receives:

#!/usr/bin/perl

package My::Mod;

use strict;
use warnings;

use Data::Dumper;
$Data::Dumper::Indent = 0;

sub new { bless [] => shift }

sub frobnicate { Dumper(\@_) }

package main;

use strict;
use warnings;

my $x = My::Mod->new;

# invoke instance method
print $x->frobnicate('High' => 42, 'Low' => 11);

# invoke class method
print My::Mod->frobnicate('High' => 42, 'Low' => 11);

# call sub frobnicate in package My::Mod
print My::Mod::frobnicate('High' => 42, 'Low' => 11);

Output:

$VAR1 = [bless( [], 'My::Mod' ),'High',42,'Low',11];
$VAR1 = ['My::Mod','High',42,'Low',11];
$VAR1 = ['High',42,'Low',11];
用心笑 2024-08-17 08:51:18

一些尚未处理的问题:

在 Perl 中,构造函数只是一个
名为new的子例程。

不完全是。将构造函数称为 new 只是一种约定。您可以随意称呼它。从 Perl 的角度来看,这个名字没有什么特别的。

祝福$self,$type;

您的两个示例都没有显式返回 bless 的结果。我希望你知道他们无论如何都是隐含地这样做的。

Some points that haven't been dealt with yet:

In Perl, the constructor is just a
subroutine called new.

Not quite. Calling the constructor new is just a convention. You can call it anything you like. There is nothing special about that name from perl's point of view.

bless $self, $type;

Both of your examples don't return the result of bless explicitly. I hope that you know that they do so implicitly anyway.

还给你自由 2024-08-17 08:51:18

如果将数组分配给散列,perl 会将数组中的交替元素视为键和值。你的数组看起来像

my @array = (key1, val1, key2, val2, key3, val3, ...);

当你将其分配给 %hash 时,你会得到

my %hash = @array;
# %hash = ( key1 => val1, key2 => val2, key3 => val3, ...);

这是 Perl 列表/哈希构造语法中的另一种说法, ",""=>" 意思是一样的。

If you assign an array to a hash, perl treats alternating elements in the array as keys and values. Your array is look at like

my @array = (key1, val1, key2, val2, key3, val3, ...);

When you assign that to %hash, you get

my %hash = @array;
# %hash = ( key1 => val1, key2 => val2, key3 => val3, ...);

Which is another way of saying that in perl list/hash construction syntax, "," and "=>" mean the same thing.

肩上的翅膀 2024-08-17 08:51:18

在 Perl 中,子例程的所有参数都通过预定义数组 @_ 传递。

shift 删除并返回 @_ 数组中的第一项。在 Perl OO 中,这是方法调用者——通常是构造函数的类名和其他方法的对象。

哈希值会展平为列表并可以通过列表进行初始化。模拟子例程的命名参数是一个常见的技巧。例如,

Employee->new(name => 'Fred Flintstone', occupation => 'quarry worker');

忽略类名(已移出),奇数元素成为散列键,偶数元素成为相应的值。

my $self = {} 创建一个新的哈希引用来保存实例数据。 bless 函数将普通哈希引用 $self 转换为对象。它所做的只是添加一些元数据,将引用标识为属于该类。

In Perl, all arguments to subroutines are passed via the predefined array @_.

The shift removes and returns the first item from the @_ array. In Perl OO, this is the method invocant -- typically a class name for constructors and an object for other methods.

Hashes flatten to and can be initialized by lists. It's a common trick to emulate named arguments to subroutines. e.g.

Employee->new(name => 'Fred Flintstone', occupation => 'quarry worker');

Ignoring the class name (which is shifted off) the odd elements become hash keys and the even elements become the corresponding values.

The my $self = {} creates a new hash reference to hold the instance data. The bless function is what turns the normal hash reference $self into an object. All it does is add some metadata that identifies the reference as belonging to the class.

在你怀里撒娇 2024-08-17 08:51:18

是的,我知道我在这里有点像死灵法师,但是......

虽然所有这些答案都很棒,但我想我应该提到驼鹿。 Moose 使构造函数变得简单(package Foo;use Moose; 自动提供一个名为 new 的构造函数(尽管如果您愿意,可以覆盖名称“new”)),但是 < a href="http://search.cpan.org/dist/Moose/lib/Moose/Manual/Construction.pod" rel="nofollow">不会剥夺任何可配置性(如果您需要的话)。

一旦我浏览了 Moose 的文档(总体来说相当不错,如果你适当地谷歌搜索的话,还有更多的教程片段),我再也没有回头。

Yes, I know that I'm being a bit of a necromancer here, but...

While all of these answers are excellent, I thought I'd mention Moose. Moose makes constructors easy (package Foo;use Moose; automatically provides a constructor called new (although the name "new" can be overridden if you'd like)) but doesn't take away any configurability if you need it.

Once I looked through the documentation for Moose (which is pretty good overall, and there are a lot more tutorial snippets around if you google appropriately), I never looked back.

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