在子程序中通过引用操作 Perl 对象
Manipulate Perl object by reference in subroutine
我有一个 Perl 程序和软件包 Worker
和 Log
。
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
创建对该引用的引用,这可能不是您想要的。您可以通过两种方式处理:
- 仅通过
$log
:
$worker->subFromWorker($scalar, $log, \@array);
- 在
subFromWorker
中取消引用 $log
,然后再对其调用函数:
$$log_ref->write('...');
您已阅读有关阻止您的程序运行的问题,但还有一些其他事项您应该注意
Perl 词法标识符和 subroutine/method 名称由字母数字和下划线组成。大写字母保留用于全局标识符,例如包名称 Worker
和 Log
.
您 use
或 require
的软件包应以语句 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.
我有一个 Perl 程序和软件包 Worker
和 Log
。
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
创建对该引用的引用,这可能不是您想要的。您可以通过两种方式处理:
- 仅通过
$log
:$worker->subFromWorker($scalar, $log, \@array);
- 在
subFromWorker
中取消引用$log
,然后再对其调用函数:$$log_ref->write('...');
您已阅读有关阻止您的程序运行的问题,但还有一些其他事项您应该注意
Perl 词法标识符和 subroutine/method 名称由字母数字和下划线组成。大写字母保留用于全局标识符,例如包名称
Worker
和Log
.您
use
或require
的软件包应以语句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.