在子例程中通过引用操作Perl对象



我有一个Perl程序和WorkerLog包。

Worker完成了几乎所有的计算,我想通过引用传递一个对象给Worker子例程,以及一些其他参数(标量和数组)。我看到过这样和这样的例子。

他们通过将@_放在subs中,然后操作对象来处理这个问题。我还发现了一种通过使用索引来操纵它们的方法,比如@{$_[i]}。问题是,当我尝试这样的代码,我得到一个错误:Can't call method "write" on unblessed reference at ...

下面的代码片段

主:

use strict;
use warnings;
use Log;
use Worker;
my $log = Log->new();
my $worker = Worker->new();
my $scalar = "SomeURLhere";
my @array = ('red','blue','white');
# I do some stuff with $log object
#...
# Now I want to pass data to the Worker
$worker->subFromWorker($scalar, $log, @array);

工作人员:

use strict;
use warnings;
package Worker;
sub new {
    my $class = shift;
    my $self = {};
    bless $self, $class;
    return $self;
}
sub subFromWorker{
    my ($self) = shift;
    my $scalar = $_[0];
    #my ($log) = $_[1];
    my @array = @{$_[2]};
    foreach my $item (@array){
        print $item;
    }
    $_[1]->write("The items from url $scalar are printed.");
    #Same thing happens if I use $log here
}

在c#中,这是以不同的方式处理的——你可以通过值或引用将参数发送给一个方法,然后在一个专门的方法中做你想做的事情(方法是预先编写的,通过引用或值来处理参数)。我认为在Perl发送使用parameter将发送引用。

对象是引用。引用是标量值。

如果你想把数组或哈希值传递给子例程,那么你通常想要传递对它们的引用——因为Perl参数传递对标量值的效果要好得多。

但是$log已经是你的对象的引用了。因此你不需要引用它。最终将一个引用传递给另一个引用。因此,当您将该参数复制到子程序中的$log中时,您就有了额外的,不必要的引用级别。

解决方法是将$log标量传递到子例程。

$worker->subFromWorker($scalar, $log, @array); # $log, not $log

您已经了解了妨碍程序工作的问题,但是还有一些其他的事情您应该注意

  • Perl词法标识符和子例程/方法名由字母数字和下划线组成。全局标识符保留大写字母,如WorkerLog等包名。

  • 您的userequire包应该以语句1;结束,以便在导入时返回true值,否则您的程序可能会编译失败。

  • 如果您正在编写的子程序恰好是方法,那么最清楚的是通过移开$self参数并复制其余部分来开始:

    my $self = shift;
    my ($p1, $p2, $p3) = @_;
    

    很少直接使用@_的元素,除非你迫切需要最小的速度奖励

  • 通常最好直接使用数组引用,而不是复制数组,特别是当数组可能很大时。

下面是我将如何编写您的程序和相关模块:

program.pl

use strict;
use warnings;
use Worker;
use Log;
my $log    = Log->new;
my $worker = Worker->new;
my $scalar = 'SomeURLhere';
my @array  = qw/ red blue white /;
$worker->worker_method($scalar, $log, @array);

Worker.pm

use strict;
use warnings;
package Worker;
sub new {
    my $class = shift;
    my $self  = {};
    bless $self, $class;
    return $self;
}
sub worker_method {
    my $self   = shift;
    my ($scalar, $log, $array) = @_;
    foreach my $item (@$array) {
        print $item, "n";
    }
    $log->write("The items from URL $scalar are printed.");
}
1;

Log.pm

use strict;
use warnings;
package Log;
sub new {
    my $class = shift;
    bless {}, $class;
}
sub write {
    my $self   = shift;
    my ($text) = @_;
    print "Logging: $textn"
}
1;

输出
red
blue
white
Logging: The items from URL SomeURLhere are printed.

更常见的模式是使用List赋值将@_一次解压缩为多个变量:

sub subFromWorker {
   my ($self, $scalar, $log_ref, $array) = @_;
   ...
}

针对您的具体问题:

my $log = Log->new();

$log已经一个引用到你的对象,使用$log创建一个引用到那个引用,可能不是你想要的。您可以通过以下两种方式处理:

  1. 仅通过$log:
    • $worker->subFromWorker($scalar, $log, @array);
  2. subFromWorker上调用函数之前取消对$log的引用:
    • $$log_ref->write('...');

最新更新