使用闭包来修改Perl BEGIN块中的类

前端之家收集整理的这篇文章主要介绍了使用闭包来修改Perl BEGIN块中的类前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
前言注意:为了便于讨论,请暂时忽略这样一个事实:使用 Class::Accessor可以实现同一目的,或者甚至只使用 Moose(在考虑代码可读性和可维护性时可能会有更好的结果) .

关于面向对象的Perl,Programming Perl一书讨论了使用闭包生成存取方法的能力.例如,这是一段有效的代码

#!perl

use v5.12;
use warnings;

# at run-time
package Person1;

my @attributes = qw/name age address/;

for my $att ( @attributes )
{
  my $accessor = __PACKAGE__ . "::$att";

  no strict 'refs'; # allow symbolic refs to typeglob

  *$accessor = sub {
    my $self = shift;
    $self->{$att} = shift if @_;
    return $self->{$att};
  };
}

sub new { bless {},shift }

package main;

use Data::Dumper;

my $dude = Person1->new;
$dude->name('Lebowski');
say Dumper($dude);

在上面的例子中,如果我没有弄错的话,该类是在运行时组成的,其访问器是在实例化类的同时创建的.这意味着对象创建将受到速度惩罚.

现在考虑以下替代方案:

#!perl

use v5.12;
use warnings;

package Person2;

BEGIN
{
  for my $att (qw/name age address/)
  {
    my $accessor = __PACKAGE__ . "::$att";

    no strict 'refs'; # allow symbolic refs to typeglob

    *$accessor = sub {
      my $self = shift;
      $self->{$att} = shift if @_;
      return $self->{$att};
    };
  }
}

sub new { bless {},shift }

package main;

use Data::Dumper;

my $dude = Person2->new;
$dude->name('Lebowski');
say Dumper($dude);

在这个版本中,组合是在一个BEGIN块内(即在编译时),我相信通过在程序的生命周期中尽快处理这个任务,我在对象实例化期间节省了时间在运行时.

一个简单的基准,

# benchmark it!
package main;

use Benchmark qw/cmpthese/;

cmpthese(-2,{
  accessors_new   => sub { Person1->new },accessors_begin => sub { Person2->new },});

这些结果似乎支持了我的理论:

Rate accessors_begin   accessors_new
accessors_begin 853234/s              --             -9%
accessors_new   937924/s             10%              --

假设到目前为止我的推理是正确的,

>比较这两种策略时还有哪些其他好处/缺点?
>依靠BEGIN块作为进行这种类操作的有效方法是一个好主意吗?
>什么时候不推荐?

解决方法

当我运行你的基准测试时,我会得到相当多的颤动,这可以解释你的差异.对于10%或更小的差异,运行几次以确定.
Rate accessors_begin   accessors_new
accessors_begin 1865476/s              --             -4%
accessors_new   1943339/s              4%              --

                     Rate accessors_begin   accessors_new
accessors_begin 1978799/s              --             -1%
accessors_new   2001062/s              1%              --

                     Rate   accessors_new accessors_begin
accessors_new   1943339/s              --             -2%
accessors_begin 1988089/s              2%              --

                     Rate accessors_begin   accessors_new
accessors_begin 1796509/s              --             -8%
accessors_new   1949296/s              9%              --

                     Rate accessors_begin   accessors_new
accessors_begin 1916122/s              --             -3%
accessors_new   1969595/s              3%              --

但实际上你所有的基准测试都是新的{bless {},shift}.对自己进行基准测试同样会强调颤抖.生成访问器的工作已经在加载代码时完成,并且从未进入,BEGIN是否阻止.

Perl没有单个编译时和运行时.相反,使用,需要或逐渐使用的每个东西都经历了它自己的编译和运行时步骤.使用Some :: Class会导致Some / Class.pm执行编译和运行时执行BEGIN,编译子例程然后执行任何其他代码.代码是在模块内的BEGIN块内部还是外部,对该模块外部的代码没有什么区别.

猜你在找的Perl相关文章