使用变量引用模块的Perl会混淆传递参数

时间:2016-05-28 15:33:02

标签: perl perl-module

使用变量引用模块时遇到问题,似乎搞乱了变量的传递:

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作为第一个变量传递的任何建议?

5 个答案:

答案 0 :(得分:2)

简短:观察到的行为来自于在包名上使用->

箭头运算符与引用或对象一起使用,该对象本身是对已经bless的数据结构的引用。 (或者使用类名,见下文。)该对象或类名作为第一个参数安静地传递,以便整个系统可以工作。请注意,问题中的包定义一个类(无法使用它创建对象)。

来自Arrow operator in perlop

  

“ - >”是一个中缀解除引用运算符,就像在C和C ++中一样。如果右侧是[...],{...}或(...)下标,则左侧必须是对数组,散列或子例程的硬引用或符号引用分别。 (或者从技术上讲,一个能够持有硬引用的位置,如果它是用于赋值的数组或哈希引用。)请参阅perlreftutperlref

继续,对这个问题直接感兴趣的陈述

  

否则,右侧是方法名称或包含方法名称或子程序引用的简单标量变量,左侧必须是对象(有福的引用)或类名称(即,包裹名字)。请参阅perlobj

因此,在与类相关的用法中,左侧可能包含类名,然后可以在其上调用类方法(或者可以只查询它)。鉴于一个类是一个包,那么这个一个包名。

问题中的情况属于此范围,因此包名称将传递给子例程。但是,根据上面的引用,似乎sub只能是方法,这不是这里的情况。所以可能真的不允许->使用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 。无论哪种方式,在非类别的包装上使用它都会让我觉得错误。

更新澄清。这个用途旨在解决加载包的模糊性。包名称保存到变量中,然后使用箭头操作符在其上调用子包。在这种情况下,必须将代码添加到sub中以处理第一个参数(包名称),无论调用如何,都通过箭头操作符提供。但是,当在一个对象上调用 时,我们必须允许一个案例,最后是一个涵盖两个不同用途的代码。我认为最好换成不涉及所有这些的设计。

如果你想使用一个包,比如作为一个库

档案TOTO.pm

prn

我已将子名更改为use warnings; use strict; use TOTO qw(prn); prn("Hello World"); ,因此它不是Perl库函数。主要脚本

TOTO::prn()

始终可以使用完全限定名TOTO。如果你想把它变成一个需要在包中多一点的类。

此软件包@EXPORT_OK默认情况下不会导出任何内容,除非要求。这就是main::设置的原因,这就是我们需要在use TOTO时列出要导入 <receiver android:name="ir.hamgam.fion.ec.mobile.services.BootCompletedReceiver"> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED"/> </intent-filter> </receiver> 的函数的原因。例如,使用perlmod

开始

答案 1 :(得分:2)

用最简单的术语来说,要创建面向对象的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

您将希望使此代码更有用,并且此主题有许多变体,但这些是基础知识

答案 2 :(得分:1)

这就是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]部分在技术上是不需要的,因为你的班级中没有构造函数(你只在类上调用方法,但如果你在类中调用方法,它会做正确的事情,如果你曾经使用对象而不必在以后改变任何东西。)

答案 3 :(得分:1)

这是问题

  

关于如何避免将TOTO作为第一个变量传递的任何建议?

你自己发现了答案。这很好用

TOTO::print('Hello World');

如果您将其称为

TOTO->print('Hello World');

然后您要求perl将print作为类方法调用,并将('TOTO', 'Hello World')作为参数传递给TOTO::print子例程

如果TOTO只是一堆子程序,那么就像你找到的那样,只需致电TOTO::totosub

答案 4 :(得分:0)

检查之间的差异:

TOTO::print("Hello World");

TOTO->print("Hello World");

这不是正确的对象表示法,因为TOTO只是一个字符串。

语法object->function(arguments) whil传递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 perlobjman perlootutman perlmodlib