tie 绑定数组

数组绑定

一个实现捆绑数组的类至少要定义方法 TIEARRAY,FETCH,和 STORE。此外还有许多可选 方法:普遍存在的 DESTROY 方法,还有用于提供 $#array 和 scalar(@array) 访问的 STORESIZE 和 FETCHSIZE 方法。另外,当 Perl 需要清空该数组时会触发 CLEAR ,而当 Perl 在一个真正的数组上需要预先扩充(空间)分配的时候需要 EXTEND。

如果你想让相应的函数在捆绑的数组上也能够运行,你还可以定义 POP,PUSH,SHIFT, UNSHIFT,SPLICE,DELETE,和 EXISTS 方法。Tie::Array 类可以作为一个基类,用于利用 FETCH 和 STORE 实现前五个函数 。(Tie::Array 的 DELETE 和 EXISTS 的缺省实现只是 简单地调用 croak。)只要你定义了 FETCH 和 STORE,那么你的对象包含什么样的数据 结构就无所谓了。

另外, Tie::StdArray 类(在标准的 Tie::Array 模块中定义)提供了一个基类,这个 基类的缺省方法假设此对象包含一个正常的数组。下面是一个利用这个类的简单的数组 捆绑类。因为它使用了 Tie::StdArray 做它的基类,所以它只需要定义那些应该以非标准 方法对待的方法。

#!/usr/bin/perl
package ClockArray;
use Tie::Array;
our @ISA="Tie::StdArray";
sub FETCH{
    my($self,$place)=@_;
    $self->[$place % 2];
}
sub STORE{
    my($self,$place,$value)=@_;
    $self->[$place % 12]=$value;
}

package main;
tie my @array,"ClockArray";
@array=("a".."z");
print "@array\n";

运行时,程序会打印结果“y z o p q r s t u v w x"。这个类提供了一个只有12个槽的数组,就像时钟上的小时一样,编号为0到11.如果请求第15个数组索引,实际上会得到3个数组索引。可以把它看做是一个旅行助手,能帮助那些不会读24小时制时钟的人。

 数组绑定方法

前面介绍的内容都很简单。下面看内部细节。为了便于说明,我们实现一个数组,这个数组的上下界在创建时确定,如果想访问越界的内容,就会产生一个异常。例如:

use BoundedArray;
tie @array,"BoundedArray",2;
@array[0]="fine";
@array[1]="good";
@array[2]="great"
@array[3]="whoa" ;#禁止,这会显示一个错误信息

这个类的声明代码如下:

package BoundedArray;
use Carp;
use strict;

为了避免以后SPLICE,我们将继承Tie::Array类;

use Tie::Array;
our @ISA=("Tie::Array");

CLASSNAME->TIEARRAY(LIST)

作为这个类的构造函数,TIEARRAY应当返回一个被祝福的引用,通过这个引用可以模拟绑定数组。下一个例子只是为了说明实际上不一定非得返回一个数组引用,这里选择一个散列引用来表示我们的对象。散列很适合作为一个通用的记录类型;散列的"BOUND"键对应的值将存储允许的上界,其"DATA"值包含实际数据。如果试图在这个类以外解引用返回的对象(不加怀疑的认为是一个数组引用),就会产生一个异常。

sub TIEARRAY{
    my $class=shift;
    my $bound=shift;
    confess "usage tie(\@ary,"BoundedArray",max_subscript)"
        if @_ || $bound=~/\D/;
    return bless {BOUND=>$bound,DATA=>[]},$class;
}

现在可以写为:

tie(@array,"BoundedArray",3);#允许最大的索引值为3;

以确保这个元素不会超过4个元素。只要访问或者存储数组的一个元素,就会像标量一样调用  FETCH 和STORE,不过有一个额外的索引参数;

SELF->FETCH(INDEX)

只要访问绑定数组中的一个元素就会运行这个方法。在对象参数后面,这个方法还需要另外一个参数;想要获取值的相应的索引。

sub FETCH{
    my($self,$index)=@_;
    if($index > $self->{BOUND}){
        confess "Array 00b;$index >$self->{BOUND}";
    }
    return $self->{DATA}[$index];
}

SELF->STORE(INDEX,VALUE)

需要设置绑定数组中的一个元素时就会调用这个方法,在对象参数后面,这个方法还需要两个参数:在哪个索引上存储值,以及在这个位置上存储什么值。例如:

sub STORE{
    my($self,$index,$value)=@_;
    if($index > $self->{BOUND}){
        confess "Array 00b;$index >$self->{BOUND}";
    }
    return $self->{DATA}[$index]=$value;
}

SELF->UNTIE

这个方法由untie触发,这个例子中并不需要这个方法。有关的注意事项见这一章后面"一个解除绑定小陷阱一节"。(在变量绑定总结后面会讲到);

SELF->DESTROY

需要撤销绑定变量并回收其内存时,perl会调用这个方法。提供了垃圾回收的语言中几乎都不需要这个方法,所以例子中没有使用这个方法。

SELF->FETCHSIZE

FETCHSIZE方法应当返回与SELF关联的绑定的数组元素中的元素总数,他等价于scalar(@array),这次通常等于$#array+1.

sub FETCHSIZE{
    my $self=shift;
    return scalar @{$self->{DATA}};
}

SELF->STORESIZE(COUNT)

这个方法将于SELF关联的绑定数组中的元素总数设置为COUNT 如果数组收缩,应当删除超出COUNT的元素。如果数组扩展,应当确保新位置(新元素)未定义。对于我们的BoundedArray类,还要确保数组扩展不会超出最初设置的限制。

sub STORESIZE{
    my($self,$count)=@_;
    if($count>$self->{BOUND}){
        confess "Array 00b:$count>$self->{BOUND}";
    }
    $#{$self->{DATA}}=$count;

SELF->EXTEND(COUNT)

perl使用EXTEND方法来指示数组可能要扩展以便保存COUNT个元素,这样可以一次分配足够的内存,而不是通过多个连续的调用来分配。由于BoundedArray有固定的上界,所以我们没有定义这个方法。

SELF->EXISTS(INDEX)

这个方法验证绑定数组中INDEX对应的元素师否存在,对于我们的BoundedArray类,在验证未超过固定上界之后,我们直接使用了perl的内置exists函数

sub EXISTS{
    my($self,$indes)=@_;
    if($index>$self->{BOUND}){
        confess "Array 00B:$index > $self->{BOUND}";
    }
    exists $self-{DATA}[$index];

SELF->DELETE(INDEX)

DELETE方法删除绑定数组SELF中INDEX对应的元素,对于我们的BoundedArray类,这个方法看上去和EXISTS几乎相同,不过这不是标准

sub DELETE{
    my($self,$index)=@_;
    if($index > $self->{BOUND}){
        confess "Array 00b:$index > $self->{BOUND}";
    }
    delete $self->{DATA}{$index};

SELF->CLEAR

清空数组时会调用这个方法,讲数组设置为一个新值列表(或一个空列表)是就会发生这种情况,不过提供给undef函数时不会清空。由于清空的BoundArray总能满足上界限制,所以这里不需要做任何检查

sub CLEAR{
    my $self=shift;
    $self->{DATA}=[];
}

如果把数组设置为一个空列表,会触发CLEAR,但是不会看到列表值。所以如果超过了上界,如下:

tie(@array,"BoundedArray",2);
@array=(1,2,3,4);

SELF->PUSH(LIST)

这个方法把LIST的元素追加到数组开头。对于我们的BoundedArray类,这个子例程类似PUSH;

SLEF->POP

POP方法删除数组的最后一个元素,并将其返回。对于BoundedArray类,这个方法只有一行代码

sub POP{my $self=shift;pop @{$self->{DATA}} }

SELF->SHIFT

SHIFT方法删除数组中的第一个元素,并将其返回,对于BoundedArray类,这个方法类似POP

SELF-SPLICE(OFFSET,LENGTH,LIST)

这个方法允许对SELF数组分片。为了模拟perl的内置splice函数,OFFSET应当是可选择的,默认为0,负值表示从数组末尾倒数。LENGTH应当是可以选择的,默认为数组的剩余长度,LIST可以为空。如果能正确的模拟perl的内置splice函数。这个方法会返回原来位于offset的length个元素的一个列表;

由于分片操作有些复杂,因此在例子中不打算定义这个方法。这里只使用Tie::Array模块的SPLICE子例程,从Tie::Array继承时既可以得到这个子例程。这样一来我们就可以用其他BoundedArray方法定义SPLICE,还能完成越界检查。


以上是整个BoundedArray类。他稍微改变了数组的语意,不过可以做的更好,需要更小的空间;


注:本文大多来源于《perl programing》这本书,也就是大骆驼!写成blog也是方便以后查询复习的时候能够快速的找到。

你可能感兴趣的:(tie 绑定数组)