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

Manipulate Perl object by reference in subroutine

我有一个 Perl 程序和软件包 WorkerLog

Worker 完成了几乎所有的计算,我想通过对Worker 子程序的引用传递一个对象,以及一些其他参数(标量和数组)。我见过像 this and this.

这样的例子

他们通过将 @_ 放入 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

然后其他一切都会正常工作。

一种更常见的模式是使用列表赋值将 @_ 一次解包到多个变量中:

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('...');

您已阅读有关阻止您的程序运行的问题,但还有一些其他事项您应该注意

  • Perl 词法标识符和 subroutine/method 名称由字母数字和下划线组成。大写字母保留用于全局标识符,例如包名称 WorkerLog.

  • userequire 的软件包应以语句 1; 结尾,以便 return a 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: $text\n"
}

1;

输出

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