【问题标题】:Manipulate Perl object by reference in subroutine在子例程中通过引用操作 Perl 对象
【发布时间】:2015-10-20 12:21:41
【问题描述】:

我有一个 Perl 程序和包 WorkerLog

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

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

下面的代码 sn-ps。

主要:

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 发送将发送参考。

【问题讨论】:

  • 当你说你有一个包Main你的意思是它是你的程序吗?它应该在*.pl 文件中
  • @Borodin 一个程序,我来修改问题。
  • "(方法是预先编写的,通过引用或值来处理参数)" 和Perl完全一样。如果您通过引用传递一个对象(尽管这样做很奇怪),那么您必须在方法中这样对待它。如果你通过\$log,那么你将不得不写${$log}->write( ... )(假设你已经用my $log = $_[1]从参数中拉出$log
  • Re “在 C# 中,这是以不同的方式处理的——你可以通过值或引用向方法发送参数”,Perl 总是通过引用传递

标签: perl object reference perl-module subroutine


【解决方案1】:

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

如果您想将数组或哈希传递到子例程中,那么您通常需要传递对它们的引用 - 因为 Perl 参数传递对标量值的效果要好得多。

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

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

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

其他一切都会正常工作。

【讨论】:

    【解决方案2】:

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

    • 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);
    

    工人.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;
    

    日志.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.
    

    【讨论】:

      【解决方案3】:

      更常见的模式是使用 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('...');

      【讨论】:

      • 我喜欢:my $self = shift; my ($scalar, $log_ref, $array) = @_;,因为 a) 它强调 $self 是一个特殊参数,b) 它留下了包含实际参数列表的 @_
      • 感谢您的建议,但就像我在问题中所写的那样,我看到了@_ 的解决方案,而另一种方法对用户更友好,至少对我自己而言。
      • @DaveCross 说得好,但我碰巧喜欢这样做。我有时会使用shift,尤其是当我有一个将其所有工作委托给另一个子(可能是私人)的子时:sub public { my $self = shift; return $self->_private(@_); }
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-05-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-06-22
      相关资源
      最近更新 更多