首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用闭包修改Perl BEGIN块中的类

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

Stack Overflow用户
提问于 2012-03-07 12:30:09
回答 3查看 370关注 0票数 7

转发注意:为了本文的讨论,让我们暂时忽略这样一个事实:通过使用https://metacpan.org/module/Class::Accessor,甚至简单地使用https://metacpan.org/module/Moose (在考虑代码可读性和可维护性时,可能会有更好的结果),就可以达到相同的目的。

关于面向对象的Perl,书Programming Perl讨论了用闭包生成访问器方法的能力。例如,这是一段有效的代码:

代码语言:javascript
复制
#!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);

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

现在考虑以下备选方案:

代码语言:javascript
复制
#!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

代码语言:javascript
复制
# benchmark it!
package main;

use Benchmark qw/cmpthese/;

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

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

代码语言:javascript
复制
                    Rate accessors_begin   accessors_new
accessors_begin 853234/s              --             -9%
accessors_new   937924/s             10%              --

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

  • 在比较这两种策略时,还存在哪些其他优点/缺点?
  • 依赖BEGIN块作为执行此类类操作的有效方法是一个好主意吗?
  • 什么时候不推荐?
EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2012-03-07 12:51:10

当我运行您的基准测试时,我会得到相当大的震动,这可以解释您的差异。对于任何10%或更少的差异,运行几次,以确保。

代码语言:javascript
复制
                     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%              --

但实际上,你所要做的就是sub new { bless {}, shift }。用同样的东西来衡量自己也会强调颤振。生成访问器的工作已经在加载代码时完成,并且从未进入它,无论是否开始阻塞。

Perl没有一个编译时和运行时.相反,used、required或evaled的每一件事都要经过它自己的编译和运行时步骤。use Some::Class使Some/Class.pm同时通过编译和运行时执行BEGIN,编译子程序,然后执行任何其他代码。无论代码在模块内部还是外部,模块内的BEGIN块对该模块外的代码都没有多大影响。

票数 10
EN

Stack Overflow用户

发布于 2012-03-07 13:19:49

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

您正确地说,访问器是在运行时创建的,但是在代码中,只在执行开始时创建了访问器一次--当然不是在实例化的时候。您可以看到构造函数的作用:

代码语言:javascript
复制
sub new { bless {}, shift }

这是相当简短和相应的快速。将BEGIN块应用到访问器构建循环中,只会将工作从运行时开始移动到编译时结束,而您没有取得任何成果。你在你的基准中得到的变化是微不足道的,我想主要是由于噪音。

我已经在我自己的系统上复制了您的基准测试,将运行时间提高到10秒,并在四次测试中得到了以下结果。添加一个BEGIN块似乎可以稍微提高性能,但这是一个极小的改进,我无法对其进行永无休止的解释。

代码语言:javascript
复制
                     Rate   accessors_new accessors_begin
accessors_new   1463771/s              --             -1%
accessors_begin 1476583/s              1%              --


                     Rate   accessors_new accessors_begin
accessors_new   1469833/s              --             -0%
accessors_begin 1472234/s              0%              --


                     Rate   accessors_new accessors_begin
accessors_new   1454942/s              --             -1%
accessors_begin 1469680/s              1%              --


                     Rate   accessors_new accessors_begin
accessors_new   1462613/s              --             -1%
accessors_begin 1473985/s              1%              --
票数 3
EN

Stack Overflow用户

发布于 2012-03-07 12:42:38

如果将包分离到它们自己的文件并使用use,那么区别就消失了:模块的代码正在编译时运行。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/9601545

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档