::和->的区别

::和->的区别

看了一段时间perl相关的资料,觉得对::和->的区别不理解,也可能就根本理解错误。请高手帮忙指正:
我写了两个文件做实验,一个是common.pm,一个是test.pl

[test.pl]
#!/usr/bin/perl -w
BEGIN{ push @INC,'/linux/src/COMMON'};
use strict;
require common;

my $result=common->internal_days("20080911","20021011");
my $day=common->month_days("20080911");

print "this is $result\n";
print "this is $day\n";

[common.pm]
#!/usr/bin/perl -w
use strict;
package common;

sub internal_days
{
  # define all the used parameters
  my($year1,$month1,$day1,$year2,$month2,$day2,$day_number,$different_day);
  if ($_[1]>$_[2])
  {
  $year1=substr($_[1],0,4);
  $month1=substr($_[1],4,2);
  $day1=substr($_[1],6,2);

  $year2=substr($_[2],0,4);
  $month2=substr($_[2],4,2);
  $day2=substr($_[2],6,2);
  }
  else
  {
  $year2=substr($_[1],0,4);
  $month2=substr($_[1],4,2);
  $day2=substr($_[1],6,2);

  $year1=substr($_[2],0,4);
  $month1=substr($_[2],4,2);
  $day1=substr($_[2],6,2);
  }   

  $day_number=$day1-$day2;
  while($month1!=$month2 or $year1!=$year2)
  {
    $month1-=1;
    if($month1==0)
    {
       $month1=12;
       $year1-=1;
    }
   
    if($month1=~[1,3,5,7,8,10,12])
    {
       $different_day=31;
    }
    else
    {
       if($month1==2)
       {
          if(${year1}%4==0)
          {
            if(${year1}%400==0)   
            {
              $different_day=29;
            }
            elsif(${year1}%100==0)
            {
              $different_day=28;
            }
          }
          else
          {
            $different_day=29;
          }
       }
       else
       {
         $different_day=30;
       }
    }
  
  $day_number=$day_number+$different_day;
  }

  return $day_number;
}

sub month_days
{
  my($year,$month,$day);

  $year=substr($_[1],0,4);
  $month=substr($_[1],4,2);

  if($month=~[1,3,5,7,8,10,12])
  {
     $day=31;
  }
  else
  {
     if($month==2)
     {
        if(${year}%4==0)
        {
          if(${year}%400==0)
          {
            $day=29;
          }
          elsif(${year}%100==0)
          {
            $day=28;
          }
        }
        else
        {
          $day=29;
        }
     }
     else
     {
       $day=30;
     }
  }
  
  return $day;
}


1;

如果test.pl中使用common->internal_days调用,能够正常返回结果,
如果改成common::internal_days,则报错说
Use of uninitialized value $month2 in numeric ne (!=) at /linux/src/COMMON/common.pm line 71

::开始我一直以为是只能使用package中的变量,看起一些资料说可以进行subroute的调用,可是换掉就报错了,不知道是我哪里使用不正确? 谢谢了!
看标题先说一点区别
$object->method
这种方式调用的话是会继承的,比如说:
你的object是cow类,method是eat,但是cow类继承于animal类,cow本身没有eat这个method
那么你使用
$object->method之后实际调用的是animal类里面的eat method

如果使用class::method的话你就写死了,不会继承的,so 如果你写成cow::eat,你就华丽的挂了
你通过

[Copy to clipboard] [ - ]
CODE:
my $result=common->internal_days("20080911","20021011");

调用,实际上调用的是

[Copy to clipboard] [ - ]
CODE:
common::internal_days("common","20080911","20021011");

所以你通过->调用传进去的参数有三个,第一个是perl自动帮你设置的(根据调用者是class还是一个object设成class的名字或者是object的名字)


而你第二次直接显示使用class::method的时候,需要自己写第一个参数,你少写了一个
哦,这样啊,我明白了! 非常感谢!
我这里还有一个疑问就是
[personal.pm]
#!/usr/bin/perl -w
package person;
use strict;

sub sleep() {
        my ($self) = @_;
        my $name = $self->{"name"};

        print("$name is person, he is sleeping\n");
}

sub study() {
        my ($self) = @_;
        my $name = $self->{"name"};

        print("$name is person, he is studying\n");
}
return 1;

[main.pl]
#!/usr/bin/perl =w
use strict;
use person;
use dog;

sub main()
{
        my $object = {"name" => "tom"};

        # 先把"tom"变为人
        bless($object, "person");
        $object->sleep();
        $object->study();

        # 再把"tom"变为狗
        bless($object, "dog");
        $object->sleep();
        $object->bark();

        # 最后,再把"tom"变回人
        bless($object, "person");
        $object->sleep();
        $object->study();
}

&main();

问题1:my $object = {"name" => "tom"};是个什么类型啊? hash么?
问题2:$object->sleep();没有传递参数,为什么sleep中的$self能得到能得到值,是不是想你上面说的,->会自动生成一个object类型的参数,然后通过获得object->("name")的值,从而得到tom这个字符串啊?
1.my $object = {}
是hash的引用
是整个object的基础
至于引用具体怎么一回事,可以看intermediate perl programming或者programming perl
2.你的理解基本是正确的
$object->sleep();
调用的对象是一个object,所以传递进去的第一个参数就是$object
在method里面通过

[Copy to clipboard] [ - ]
CODE:
my $name = $self->{"name"};

取得$name


其实对象就是引用,只不过对象是和特定的class绑定在一起的(通过bless操作),你通过

[Copy to clipboard] [ - ]
CODE:
ref $object

可以知道该object属于什么哪一个class
引用我刚才看了是怎么回事了,非常感谢!

麻烦你再帮我看看
#!/usr/bin/perl -w
BEGIN{ push @INC,'/linux/src/COMMON'};
package test;
use strict;
my @EXPORT;
my @ISA;

require Exporter;
@ISA = qw(Exporter);
@EXPORT = qw(common);

#require common;

#my $result=common::internal_days("common","20080911","20021011");
#my $day=common::month_days("common","20080911");
my $result=common->internal_days("20080911","20021011");
my $day=common->month_days("20080911");

我把test修改了一下,内容如上,但是报错说
Can't locate object method "internal_days" via package "common" (perhaps you forgot to load "common"?) at ./test line 16.

问题1:我用require可以,为什么通过export就不行了啊?
问题2:如果某个module要被其他程序使用,是不是必须要定义为package啊,而且还是要同名的? 如果没有定义为package是不是就不能通过require来引入而只能使用export来引起如啊? 我刚才试着把package common这句去掉就不行了,甚至改为不同名的也不行。
问题3:perl继承是通过@ISA来实现的吧,该数组中的内容是有顺序的,是不是可以这样理解:
@ISA=(pack1,pack2,pack3)
则pack1是pack2的super,pack2是pack3的super

谢谢了!
1.EXPORT和ISA是包变量,也就是说是全局的,不可用my来定义
还有你的EXPORT的概念有错误,你要使用一个包用use或者require
你把这段

[Copy to clipboard] [ - ]
CODE:
#!/usr/bin/perl -w
BEGIN{ push @INC,'/linux/src/COMMON'};
package test;
use strict;
my @EXPORT;
my @ISA;

require Exporter;
@ISA = qw(Exporter);
@EXPORT = qw(common);

改成

[Copy to clipboard] [ - ]
CODE:
#!/usr/bin/perl -w
BEGIN{ push @INC,'/linux/src/COMMON'};
package test;
use strict;
use common;

就一点问题都没有了
至于@EXPORT这些是写在common这个包里面的,比如你这样写

[Copy to clipboard] [ - ]
CODE:
package common;
use base qw ( Exporter );
our @EXPORT = qw ( internal_days);
...

那么你在其他包里面通过

[Copy to clipboard] [ - ]
CODE:
use common;

调用时internal_delay这个函数已经export到当前包(main)里面了
你可以直接用

[Copy to clipboard] [ - ]
CODE:
internal_delay (argc)

调用
而不必麻烦的写上common::internal_delay(argc)
于此相关的还有一个@EXPORT_OK的包变量(全局)
2.见1,你把1了解了,2就不成问题了
3.错
our @ISA = qw ( parent1 parent2)
这个仅涉及继承的顺序,@ISA中的都是父类,区别仅在于搜寻的顺序是从左到右递归搜索,因而从某种意义上来说,左边的父类优先级较高,
比如你没有定义eat这个方法
但是parent1和parent2都定义了eat方法
则实际使用的是parent1的eat方法
具体的搜寻方法如下(其中还会有UNIVERSAL和AUTOLOAD,可先直接无视):

QUOTE:
When you invoke a method methname on an invocant of type classname, Perl tries six different ways to find a subroutine to use:
1.        First, Perl looks in the invocant's own package for a subroutine named classname::methname. If that fails, inheritance kicks in, and we go to step 2.
2.        Next, Perl checks for methods inherited from base classes by looking in all parent packages listed in @classname::ISA for a parent::methname subroutine. The search is left-to-right, recursive, and depth-first. The recursion assures that grandparent classes, great-grandparent classes, great-great-grandparent classes, and so on, are all searched.
3.        If that fails, Perl looks for a subroutine named UNIVERSAL::methname.
4.        At this point, Perl gives up on methname and starts looking for an AUTOLOAD. First, it looks for a subroutine named classname::AUTOLOAD.
5.        Failing that, Perl searches all parent packages listed in @classname::ISA, for any parent::AUTOLOAD subroutine. The search is again left-to-right, recursive, and depth-first.
6.        Finally, Perl looks for a subroutine named UNIVERSAL::AUTOLOAD.

再罗嗦两句,自己写了个例子

[Copy to clipboard] [ - ]
CODE:
package first;
require Exporter;
our @ISA = qw (Exporter);
our @EXPORT = qw (  $lover );
our @EXPORT_OK = qw ( kernel $lover );
our $VERSION = 1.00;
sub kernel {
  print "This is kernel\n";
  }

our $lover = "secret";
1;

注意$lover这个变量是包变量,用my定义的在包外部是不可见的,这也是perl唯一的私有变量,因为包变量或者方法你都可以显示的访问
这样我使用的时候就可以通过

[Copy to clipboard] [ - ]
CODE:
use first;

来使用,前提是你要指明搜索路径,可以在BEGIN块中改写@INC
这样在当前包里面所有处于first包中@EXPORT中的变量或者方法都是可见的
所以我可以

[Copy to clipboard] [ - ]
CODE:
print $lover,"\n";

来访问first包里面的$lover变量,当然你也可以显示的说

[Copy to clipboard] [ - ]
CODE:
print $first::lover,"\n";

好了,这个就是@EXPORT的作用,仅仅指明其他包调用时默认import进去的变量,使用

[Copy to clipboard] [ - ]
CODE:
use package;

或者

[Copy to clipboard] [ - ]
CODE:
use package (:DEFAULT);

好了,问题来了,比如说现在你想让kernel这个subroutine可见,那怎么办呢?
方法就是显示指明你想import的东西(默认import的是@EXPORT里面的东西),如下:

[Copy to clipboard] [ - ]
CODE:
use first qw ( kernel );

这样,$lover这个变量就不可见了,必须使用
$first::lover来访问,从这个意义上来说,perl的封装性不是很好,你总是可以通过制定全名来访问某个包里面的包变量或者方法


所有你显示import的变量都必须在@EXPORT或者@EXPORT_OK里面定义,否则compile的时候就报错
我在common.pm中添加了如下代码:
use base qw(Exporter);
#our @ISA = qw(Exporter);
our @EXPORT = qw(internal_days);
......

其他的

test.pl内容如下:
#!/usr/bin/perl -w
BEGIN{ push @INC,'/linux/src/COMMON'};
#package test;
use strict;
#my @EXPORT;
#my @ISA;

#require Exporter;
#@ISA = qw(Exporter);
#@EXPORT = qw(common);

#my $result=common::internal_days("common","20080911","20021011");
#my $day=common::month_days("common","20080911");
my $result=internal_days("20080911","20021011");
my $day=month_days("20080911");

print "this is $result\n";
print "this is $day\n";

报错如下:
Undefined subroutine &main::internal_days called at ./test line 16.