Chinaunix首页 | 论坛 | 博客
  • 博客访问: 511873
  • 博文数量: 138
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 716
  • 用 户 组: 普通用户
  • 注册时间: 2015-03-03 21:48
文章分类

全部博文(138)

文章存档

2019年(1)

2017年(5)

2016年(99)

2015年(33)

我的朋友

分类: LINUX

2015-12-19 17:25:54

浅谈 typeglob

本文是对《 advanced perl programming  edition 2 中有关 typeglob 的叙述,但不是一字一句的直译,又不能讲就是意译,因为加了一点个人浮浅的理解,所以叫浅谈。

 

 

符号表

当程序使用一个全局变量时, perl 解析器会在一个符号表中查找这个变量名。可以这么认为:符号表完成了变量的名字到实际存储区域的映射。

                           Symbol table

                           |    A     | -------- à | 3 |

                           |    B     |--------- à | 2 | 

                           |    C     |

                           |    D     |

 1

请注意,是变量的名称而不是变量,这一点很特别,可以说符号表中 a 映射了一个到 $a 的内存区域,实际的情况更复杂。 perl 中有几种基本数据结构: $a @a  %a  &a 和文件或目录句柄 a ,它们都有同样的变量名称只是前缀不一样,于是就有了 glob 的概念。 

o_2.jpg 

         2

正如上图所示符号表把 b 映射一个 glob 。对 glob 的描述大概可以这么讲:它是包含了各个名为 b 的变量的引用的 hash 结构,它叫做 *b 

*b{SCALAR}=\$b;

*b{HASH}=\%b;

*b{ARRAY}=\@b;

…….

够诡异的了!

 

别名

很明显 glob 把名称与引用无情地分开了,好处大概就是可以很方便地取别名。简单地把 *b 赋值给 *c ,就是 *c=*b ,产生地结果就是这样: 

o_3.jpg 
                                      3


两个名称指向了同一个 glob ,现在这个 glob 既叫 *b 又叫 *c 

现在应该有些思路了,举个例子说: %c 地解析过程可以这么认为, perl 先在符号表中找到 c 所映射的 glob ,然后在 glob 中找到前缀为 % 的引用,最后返回存储位置。最常见体现这个思想的代码如下。

package Some::Module;

  use base ‘Exporter’;

  our @EXPORT=qw(useful);

  sub useful{42;}

Exporter 的作用就是将 useful 从包中传出到调用者那里。基本的工作原理如下。

  package Some::Module;

  sub useful{42;}

  sub import{

  no strict ‘refs’;

  *{call().”::useful”} = *useful;

  }

import 子程序在包被 use 时将自动被调用。在调用者代码中的 useful 子程序最终将被指向 Some::Module::useful 

o_4.jpg 
                                 4

之所以要用 no strict ‘ref’; 是因为如果调用者的代码中使用了 use strict 的话,将产生错误。把上面的代码再简单点比喻一下就是:

  $answer =42;

  $variable = “answer”;

  print ${$variable};

一个道理。

 

分解 glob

在上面的这个例子中,我们将 useful 的名称映射到了 Some::Module::useful  glob (或者说为 Some::Module::useful  glob 取了个别名),这会带来一些影响。比如说:

  use Some::module;

  our $useful=”Some handy string”;

  print $Some::Modile::useful;

因为 useful  Some::Modile::useful 映射同一个 glob 的关系,输出的结果是 ”Some handy string” 。但实际的情况是,我们只希望 useful 指向子程序Some::Modile::useful ,而不是整个 glob  Perl 提供了映射部分引用的办法。如果是希望仅仅映射标量或数组的话,可以这样:

  ${caller(  )."::useful"} = $useful;

  @{caller(  )."::useful"} = @useful;

但是对于子程序,如果也这么做:

  &{caller(  )."::useful"} = &useful;

首先 perl 会运行 &usefule 得到 42 ,然后将 42 赋值给运行 &{caller(  )."::useful"} 所产生的结果,但 &{caller(  )."::useful"} 根本是不存在的,于是错误产生了。为了解决这个问题, glob 这个诡异的机构提供了一个重载过的“ = ”方法。像 *b=\@c 的操作可以给 *b 添加一个到 @c 的引用。 @b  @c 任何一方的变化都将反映对方上。

o_5.jpg 

 5

*b 中除了又一个引用指向 @c 外,其他的引用都没有变化。正是有了“ = ”这个方法,我们可以随心所欲在 glob 中指定引用。

  *a = \"Hello";

  *a = [ 1, 2, 3 ];

  *a = { red => "rouge", blue => "bleu" };

  print $a;        # Hello

  print $a[1];     # 2

  print $a{"red"}; # rouge

后一个对 *a 的赋值并没有替换前面的赋值,只是加入了个不同类型的引用。有一个不得不讲的例外,如果 *a 被加入了一个到常量的引用,那么该变量的值将是不能改变的。

  *a=\1234;

  $a=10;  

这么做是徒劳的, perl 将返回“ modification of a ready-only value attempt ”的错误信息。

现在可以着手解决本节开始时遇到的那个问题了:

  sub useful{42}

  sub import{

no strict ‘refs’;

*{caller().”::userful”} = \&useful;

}

这已经跟 Exporter 的实际工作原理很接近了。

 

Exporter 核心代码的分析

  my $pkg = shift;

  my $callpkg = caller($ExportLevel);  $ExportLevel = 0

  foreach $sym(@imports){

(*{${callpkg}::$sym}) = \&{“${pkg}::$sym”}, next)

  unless $sym =~ s/^(\W)//;

  $style = $1;

  *{${callpkg}::$sym}) =

$style eq ‘&’ ? \&{“${pkg}::$sym”} :

$style eq ‘$’ ? \${“${pkg}::$sym”} :

$style eq ‘@’ ? \@{“${pkg}::$sym”} :

$style eq ‘%’ ? \%{“${pkg}::$sym”} :

$style eq ‘*’ ? *{“${pkg}::$sym”} :

do {require Carp; Carp::croak(Can’t export symbol:$sym)};

  }

我们通过 @Export 数组传入需要输出的变量。如果变量不带前缀,那么直接在最顶层调用名称的 glob 中加入被引用模块相应子程序的引用。如果有前缀,那么将前缀去掉。

  (*{${callpkg}::$sym}) = \&{“${pkg}::$sym”}, next)

  unless $sym =~ s/^(\W)//;

去掉的前缀将被赋值给 $style ,然后检验 $style 的类型,并具此返回相应类型的引用。

  $style = $1;

  *{${callpkg}::$sym}) =

$style eq ‘&’ ? \&{“${pkg}::$sym”} :

$style eq ‘$’ ? \${“${pkg}::$sym”} :

$style eq ‘@’ ? \@{“${pkg}::$sym”} :

$style eq ‘%’ ? \%{“${pkg}::$sym”} :

$style eq ‘*’ ? *{“${pkg}::$sym”} :

do {require Carp; Carp::croak(Can’t export symbol:$sym)};

如果传入的值与以上各种情况都不匹配,那么就调用 croak 中止顶层程序。

 

使用 glob 建立子程序

 glob 分配一个到匿名子程序的引用是别名技术在高级 perl 编程中的一个普遍应用。举个例子:有一个叫 Data::BT::PhoneBill 的模块,它被用于在英国电信公司的电话帐单服务中检索数据。这个模块将一个电话中的信息按照逗号分开,并将它们对象化。

  package Data::BT::PhoneBill::_Call;

  sub new{

  my ($Class,@data) = @_;

  bless \@data, $Class;

}

sub installation {shift->[0]}

sub line {shift->[1]}

…..

这样做的结果是不大好维护。如果我们打算在在数据的开头处添加一个新的条目,那么就需要修改大半块代码,将数组编号集体下移。为了避免这种情况的发生,应用 hash 来替换不方便的 array 

  our @field = qw(type installation line chargecard _data time destination _number duration _rebat cost);

  sub new{

my ($class @data) = @_;

bless {map {$field[$_] => $data[$_]} 0..$#field => $class;}

  }

  sub type {shift->{type}}

  sub installation {shift->{installation}}

  ….

只需要在代码中加入重复的 3 个词,我们就可以为模块添加一个新的条目。这比上面那个方便不少,但是如果待加入的条目名称是这样的:friend_and_family_duscount ,那么重复 3 遍的工作也够麻烦的了。于是就要用到 glob 

  Foreach my $f(@field){

    no strict ‘refs’;

*f = sub { shift->{$f}};

  }

阅读(1262) | 评论(0) | 转发(0) |
0

上一篇:perldoc 问题解决办法

下一篇:OpenIOC

给主人留下些什么吧!~~