gpt4 book ai didi

perl - 使用闭包修改 Perl BEGIN block 中的类

转载 作者:行者123 更新时间:2023-12-01 17:42:13 25 4
gpt4 key购买 nike

转发注释: 为了便于讨论,请暂时忽略这样一个事实:通过 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 block 内(即在编译时)进行的,我相信通过在生命周期中尽快处理此任务对于程序,我在运行时实例化对象期间节省了时间。

一个简单的基准

# 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 block 作为执行此类类操作的有效方法是个好主意吗?
  • 什么时候不推荐?

最佳答案

当我运行你的基准测试时,我得到了相当多的抖动,这可能是你的差异的原因。对于任何 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% --

但实际上,您所进行的基准测试只是sub new { bless {}, shift }。对同一事物进行自身基准测试会强调颤振。当代码加载时,生成访问器的工作就已经完成,并且永远不会进入其中,无论是否是 BEGIN block 。

Perl 没有单一的编译时和运行时。相反,used、required 或 evaled 的每件事都会经历其自己的编译和运行时步骤。 use Some::Class 导致 Some/Class.pm 经历编译和运行时执行 BEGIN,编译子例程,然后执行任何其他代码。无论代码位于模块内的 BEGIN block 内部还是外部,与该模块外部的代码几乎没有什么区别。

关于perl - 使用闭包修改 Perl BEGIN block 中的类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9601545/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com