Perl 使用变量来引用模块会弄乱传递参数
Perl using a variable to reference a module messes up passing parameters
我在使用变量引用模块时遇到问题,似乎弄乱了变量的传递:
TOTO.pm
package TOTO;
use Data::Dumper;
sub print {
print Dumper(@_);
}
Perl 程序
package main;
TOTO::print('Hello World');
print ">>>>>>>>>>>\n";
my $package = 'TOTO';
$package->print('Hello World');
输出为:
$VAR1 = 'Hello World';
>>>>>>>>>>>
$VAR1 = 'TOTO';
$VAR2 = 'Hello World';
关于如何避免将 TOTO
作为第一个变量传递的任何建议?
这就是 Perl 的包系统的工作原理。您需要在被调用的子程序中自行处理。您无法在通话前更改它。
sub print {
# special variable __PACKAGE__ contains "TOTO"
if ($_[0] eq __PACKAGE__ || ref $_[0] eq __PACKAGE__){
shift; # throw away class/object
}
print Dumper(@_);
}
技术上不需要 ref $_[0]
部分,因为您的 class 中没有构造函数(您只调用 class 上的方法,但它会如果您确实使用对象而无需稍后更改任何内容,则只需做正确的事即可。
简短: 观察到的行为来自对包名称使用 ->
。
箭头运算符与引用或对象一起使用,对象本身是对已 bless
编辑到其 class 中的数据结构的引用。 (或使用 class 名称,见下文。)该对象或 class 名称作为第一个参数悄悄传递,以便整个系统正常工作。请注意,问题中的包 而不是 定义了 class (不能用它创建对象)。
"-> " is an infix dereference operator, just as it is in C and C++. If the right side is either a [...] , {...} , or a (...) subscript, then the left side must be either a hard or symbolic reference to an array, a hash, or a subroutine respectively. (Or technically speaking, a location capable of holding a hard reference, if it's an array or hash reference being used for assignment.) See perlreftut and perlref.
它继续,对这个问题有直接兴趣的陈述
Otherwise, the right side is a method name or a simple scalar variable containing either the method name or a subroutine reference, and the left side must be either an object (a blessed reference) or a class name (that is, a package name). See perlobj.
因此在与 classes 相关的使用中,左侧可能包含 class 名称,然后可以在其上调用 class 方法(或者它可以只是查询)。假设 class 是一个包,那么这个 是 一个包名。
问题中的情况属于这种情况,因此将包名称传递给子程序。但是,根据上面的引述,sub 似乎只能是 method,这里不是这种情况。所以可能真的应该禁止使用 ->
。无论哪种方式,在不是 class 的包裹上使用它都让我感到误会。
更新说明。此用途旨在解决加载包的歧义。包名称被保存到一个变量中,然后使用箭头运算符对其调用子程序。在这种情况下,代码必须添加到 sub 以处理第一个参数(包名称),无论调用如何,由箭头运算符提供。但是,我们将不得不允许这样一种情况,即 this is 在一个对象上调用,最终得到一个涵盖两种不同用途的代码。我相信还是换个不涉及这一切的设计比较好。
如果你想使用一个包,比如一个库
文件TOTO.pm
pacakge TOTO;
use Exporter;
our (@ISA, @EXPORT_OK);
@ISA = ('Exporter');
@EXPORT_OK = qw(prn); # This can be asked for by user of package
use Data::Dumper;
sub prn {
print Dumper(@_);
}
1; # important for 'require' when this is used
我已将子名称更改为 prn
,这样它就不是 Perl 库函数了。主脚本
use warnings;
use strict;
use TOTO qw(prn);
prn("Hello World");
完全限定名称 TOTO::prn()
始终可以使用。如果你想让它成为一个 class,那将需要更多的包。
这个包,TOTO
,默认情况下不导出任何东西,除非被要求。这就是 @EXPORT_OK
设置的内容,这就是为什么我们需要列出要在 use TOTO
时导入到 main::
的函数。例如,从 perlmod
开始
检查两者之间的差异:
TOTO::print("Hello World");
和
TOTO->print("Hello World");
这不是正确的对象表示法,因为 TOTO
只是一个字符串。
语法 object->function(arguments)
将 object
作为第一个参数传递,存储为 $this
,示例。
sub print {
my $this = shift @_;
print Dumper(@_);
}
可以做这份工作(即使不是祝福对象)。
试试这个:
package TOTO;
use Data::Dumper;
sub new { return bless {}, shift; }
sub print {
my $self = shift @_;
if ( scalar $self =~ /=HASH\(/ ) {
print Dumper(@_);
} else {
print Dumper($self);
}
}
package main;
my $package = TOTO->new();
$package->print("Hello World");
TOTO::print("Hello World");
这可能输出:
$VAR1 = 'Hello World';
$VAR1 = 'Hello World';
并查看 man perlobj
、man perlootut
和 man perlmodlib
用最简单的术语来说,要创建一个面向对象的 TOTO
模块,您必须创建一个文件 TOTO.pm
,其中至少包含一个构造子例程 new
package TOTO;
sub new {
bless {};
}
sub print {
print "I am a TOTO object\n";
}
1;
该代码必须保存在名为 TOTO.pm
的文件中,该文件必须与源 package TOTO
中的名称相匹配
然后您可以编写一个使用该模块的程序,比如 main.pl
。例如
use strict;
use warnings 'all';
use TOTO;
my $object = TOTO->new;
$object->print;
然后您创建了一个新的 TOTO
对象,说明它是什么
如果我运行
$ perl main.pl
我得到了输出
I am a TOTO object
您会想让这段代码更有用,而且这个主题有很多变体,但这些是基础
问题来了
Any advice on how to avoid having TOTO passed as the first variable?
您自己找到了答案。这工作正常
TOTO::print('Hello World');
如果你称它为
TOTO->print('Hello World');
然后你要求 perl 调用 print
作为 class 方法并将 ('TOTO', 'Hello World')
作为参数传递给 TOTO::print
子例程
如果 TOTO
只是一堆子程序,那么,如您所见,只需调用 TOTO::totosub
我在使用变量引用模块时遇到问题,似乎弄乱了变量的传递:
TOTO.pm
package TOTO;
use Data::Dumper;
sub print {
print Dumper(@_);
}
Perl 程序
package main;
TOTO::print('Hello World');
print ">>>>>>>>>>>\n";
my $package = 'TOTO';
$package->print('Hello World');
输出为:
$VAR1 = 'Hello World';
>>>>>>>>>>>
$VAR1 = 'TOTO';
$VAR2 = 'Hello World';
关于如何避免将 TOTO
作为第一个变量传递的任何建议?
这就是 Perl 的包系统的工作原理。您需要在被调用的子程序中自行处理。您无法在通话前更改它。
sub print {
# special variable __PACKAGE__ contains "TOTO"
if ($_[0] eq __PACKAGE__ || ref $_[0] eq __PACKAGE__){
shift; # throw away class/object
}
print Dumper(@_);
}
技术上不需要 ref $_[0]
部分,因为您的 class 中没有构造函数(您只调用 class 上的方法,但它会如果您确实使用对象而无需稍后更改任何内容,则只需做正确的事即可。
简短: 观察到的行为来自对包名称使用 ->
。
箭头运算符与引用或对象一起使用,对象本身是对已 bless
编辑到其 class 中的数据结构的引用。 (或使用 class 名称,见下文。)该对象或 class 名称作为第一个参数悄悄传递,以便整个系统正常工作。请注意,问题中的包 而不是 定义了 class (不能用它创建对象)。
"-> " is an infix dereference operator, just as it is in C and C++. If the right side is either a [...] , {...} , or a (...) subscript, then the left side must be either a hard or symbolic reference to an array, a hash, or a subroutine respectively. (Or technically speaking, a location capable of holding a hard reference, if it's an array or hash reference being used for assignment.) See perlreftut and perlref.
它继续,对这个问题有直接兴趣的陈述
Otherwise, the right side is a method name or a simple scalar variable containing either the method name or a subroutine reference, and the left side must be either an object (a blessed reference) or a class name (that is, a package name). See perlobj.
因此在与 classes 相关的使用中,左侧可能包含 class 名称,然后可以在其上调用 class 方法(或者它可以只是查询)。假设 class 是一个包,那么这个 是 一个包名。
问题中的情况属于这种情况,因此将包名称传递给子程序。但是,根据上面的引述,sub 似乎只能是 method,这里不是这种情况。所以可能真的应该禁止使用 ->
。无论哪种方式,在不是 class 的包裹上使用它都让我感到误会。
更新说明。此用途旨在解决加载包的歧义。包名称被保存到一个变量中,然后使用箭头运算符对其调用子程序。在这种情况下,代码必须添加到 sub 以处理第一个参数(包名称),无论调用如何,由箭头运算符提供。但是,我们将不得不允许这样一种情况,即 this is 在一个对象上调用,最终得到一个涵盖两种不同用途的代码。我相信还是换个不涉及这一切的设计比较好。
如果你想使用一个包,比如一个库
文件TOTO.pm
pacakge TOTO;
use Exporter;
our (@ISA, @EXPORT_OK);
@ISA = ('Exporter');
@EXPORT_OK = qw(prn); # This can be asked for by user of package
use Data::Dumper;
sub prn {
print Dumper(@_);
}
1; # important for 'require' when this is used
我已将子名称更改为 prn
,这样它就不是 Perl 库函数了。主脚本
use warnings;
use strict;
use TOTO qw(prn);
prn("Hello World");
完全限定名称 TOTO::prn()
始终可以使用。如果你想让它成为一个 class,那将需要更多的包。
这个包,TOTO
,默认情况下不导出任何东西,除非被要求。这就是 @EXPORT_OK
设置的内容,这就是为什么我们需要列出要在 use TOTO
时导入到 main::
的函数。例如,从 perlmod
检查两者之间的差异:
TOTO::print("Hello World");
和
TOTO->print("Hello World");
这不是正确的对象表示法,因为 TOTO
只是一个字符串。
语法 object->function(arguments)
将 object
作为第一个参数传递,存储为 $this
,示例。
sub print {
my $this = shift @_;
print Dumper(@_);
}
可以做这份工作(即使不是祝福对象)。
试试这个:
package TOTO;
use Data::Dumper;
sub new { return bless {}, shift; }
sub print {
my $self = shift @_;
if ( scalar $self =~ /=HASH\(/ ) {
print Dumper(@_);
} else {
print Dumper($self);
}
}
package main;
my $package = TOTO->new();
$package->print("Hello World");
TOTO::print("Hello World");
这可能输出:
$VAR1 = 'Hello World';
$VAR1 = 'Hello World';
并查看 man perlobj
、man perlootut
和 man perlmodlib
用最简单的术语来说,要创建一个面向对象的 TOTO
模块,您必须创建一个文件 TOTO.pm
,其中至少包含一个构造子例程 new
package TOTO;
sub new {
bless {};
}
sub print {
print "I am a TOTO object\n";
}
1;
该代码必须保存在名为 TOTO.pm
的文件中,该文件必须与源 package TOTO
中的名称相匹配
然后您可以编写一个使用该模块的程序,比如 main.pl
。例如
use strict;
use warnings 'all';
use TOTO;
my $object = TOTO->new;
$object->print;
然后您创建了一个新的 TOTO
对象,说明它是什么
如果我运行
$ perl main.pl
我得到了输出
I am a TOTO object
您会想让这段代码更有用,而且这个主题有很多变体,但这些是基础
问题来了
Any advice on how to avoid having TOTO passed as the first variable?
您自己找到了答案。这工作正常
TOTO::print('Hello World');
如果你称它为
TOTO->print('Hello World');
然后你要求 perl 调用 print
作为 class 方法并将 ('TOTO', 'Hello World')
作为参数传递给 TOTO::print
子例程
如果 TOTO
只是一堆子程序,那么,如您所见,只需调用 TOTO::totosub