perl 数据结构的构造

哪位大侠能给些perl的数据结构的例子?我不知道在perl中如何嵌套数据结构。谢谢

不论使用什么语言来设计程序,数据结构都不能忽视,因为数据结构是你整个程序的基础元素。
使用PERL相对与其他的语言而言,数据的定义似乎更加容易。因为PERL里面的数据类型似乎只有3种表现形式:标量,数组,hash.标量不用说,是最简单和基本的形式,数组和hash则相对复杂和强大一些。一般而言,使用数组可以实现类似堆栈、队列、链表之类的数据结构,而hash则可以实现记录、查询表、或者更为复杂的结构。
因此,我们首先面临的问题是,既然我们已经应用@array,%hash CREATE了我们复杂的数据结构,我们如何去应用,我如何去存取这些复杂结构中的每个数据元素?我刚刚写程序的时候,这点常常让我迷惑,有时候不得不写一些TESTING CODE去验证我的代码,有时候觉得真是一件痛苦的事情,因为,往往正式的代码没有写几行,而相应的测试代码是它的几倍。(BTW,根据XP的准则,我这样做到也是很正确,不过那时并没有知道XP)

下面简单介绍一些常用的赋值形式:

1)2维数组
$foo[$row][$col]=’test’;
这是典型的两维ARRAY,头一个$ROW,索引出的实际上是一个数组的引用,而后一个$COL则是对这个数组引用复原后索引到的真实数组元素的地址。
以上,数组的索引都是整数,多维数组实际上就是数组的数组,我这样认为。

2)HASH OF ARRAY
$foo{’night’}[1]=”8 Mar “;
这是一个数组的hash,每个hash的KEY 对应的值是一个数组的引用。因此,你可以用这种形式来构造一条记录,hash的key是记录的关键子,数组是的元素就是这个记录中的每个字段。

3) $my_friends[$i]{’name’}=”Night Sailer”;
$my_friends[$i]{’age’}=24;

这种是hash的数组,每个数组的元素都是一个hash的引用。同2)一样,这种结构可以用来创建记录,
与2)不同的是,字段的个数与名称都是已经确定了,当然,简单明了也是这种结构的好处。

4) $lookup{’night’}{’company’}=’nightsailer’;
$lookup{’night’}{’home adress’}=’Beijing Sanlihe road’;
这是hash of hash,前面hash的key中存的是另一个hash的引用。
这种结构是一种典型的查询表了,你可以根据名字索引到某个记录,然后根据字段名索引到相应的值。

5)$members{’linux’}{’progamming’}[0]{’name’}=’Perl’;
$members{’linux’}{’progamming’}[0]{’function’}=’gettime’;

这是一个比较复杂的结构,一般来说,记录中某个字段或元素又是引用了另外的一个结构的。
依次类推,使用array和hash就可以构造出极其复杂的数据结构。

6)$crazy->{FH}->print(&{$crazy->{myfun}}($crazy->{LIST}[0]))

这是一个很疯狂的数据结构,是吧。
这个结构首先是一个hash,hash中的每个key 分别存放着一个文件句柄,一个函数的引用,一个列表的引用,上面的代码,就是用列表中的第一个元素作为参数回调myfun函数,然后把输出结果打印到记录中指定的文件句柄,也就是打印到一个文件中去。

呵呵,有一些头疼是吧,下面是一些提示和技巧,你可以不遵守这些约定,不过如果你作了,会减少你调试时候的麻烦:

1.总是使用strict

使用strict的确失去了一些随意性的乐趣,不过却可以给你减少许多不必要的麻烦。比如,变量名的拼写错误,在该用hash或着array的时候使用了scalar.

2.象类似push()这种需要数组的地方,一定需要一个@,如:
push @{$a[3]},@new_list;
千万不要写成:
push $a[3],@new_list;

3.象类似keys()这种需要hash的地方,一定需要一个%,如:
foreach $k (keys %{ $h{”key”} }) { … }
千万不要写成:
foreach $k (keys $h{”key”} ) { … }

4.当你需要存储指针的时候,不要往你的结构中存储指向已存在的数据的指针,而是用这些数据重新生成一个新的结构,存储这个匿名的指针。不好懂,例子:
while ( <> ) {
@fields = split;
push @a, [ @fields ];
}

这是创建一个2维数组,按照行号,词来索引。你不要使用\来强制引用,相反的,你应该使用[]{}分别来生成匿名的数组和hash的引用。为什么要这样,因为当你使用递归之类的时候,这种做法保证你的处理能够按照你的设想进行。

5.不要过于炫耀你的理解能力,

不要写 $$a[$i] 当你是想 ${$a[$i]}.

不要写 @$a[$i] 当你是想 @{$a[$i]}.

因为并不是所有的情况下都可以运行良好

不要写 $$a[$i] 当你是想 $a->[$i].
因为这容易给别人带来过多的猜测和困惑,如果你的记忆力没有那么好,也会给你带来麻烦。

6.记住:
$a[$i] 是 @a 的 第 i 个元素。
$a->[$i] 是 $a 所指向的那个数组的第 i 个元素。
使用strict 可以帮助你避免这些错误。

7.不要写:
@ { $a[$i] } = @list

更好的写法是:
$a[$i] = [ @list ]

虽然都一样,但是前者会令比你智商低的人感到痛苦万分。

8.适当使用->和 {},当你使用引用和反引用的时候。
因为这样会使别人,perl,甚至你自己都更加清楚你想要什么。

print ${ $a[$i] }
反引用一个标量引用

pop @{ $a[$i] }
反引用一个数组引用

@k = keys %{ $a[$i] }
反引用一个HASH引用

$retval = &{ $a[$i] }( )
反引用一个code引用

*{ $a[$i] } = \&func
反引用一个 GLOB(符号表) 引用

PELR的数据结构2-1

上次简单介绍了一下PELR的数据结构方面的知识,这次展开讨论一下。
从语言本身的角度来讲,5.0以前的PERL缺乏对复杂的数据结构提供
语言级别的支持。象C,可以通过STRUCT来定义复杂的数据结构,即使象VB这样的语言,也可以使�br>肨YPE 来创建新的数据类型。5.0以后通过对引用的加强,我们现在也
可以实现复杂的数据结构了,比如多维数组。你可以简单的使用下面的
语句来遍历一个三维数组:

for $i(0 .. 20){

for $j(0..20){

for $k(0..20){

$array[$i][$j][$k]=$i*$j+$k

}

}

}

很简单吧。不过,事情远远没有那么简单。对于复杂的数据结构,
如何去打印它,如何把它传递给其他的处理函数,如何从函数中返回这种复杂的数据结构,
如何判断是否是一个对象,如何把它序列化(存盘),之后如何复员….
呵呵,很快你就要陷入疯狂的状态了。
不过,事情源源没有那么糟糕的,万事开头难。
对于这些复杂的数据结构,大致可以分为:

* 数组的数组
* hashes 的数组
* 数组的 hashes
* hashe of hashes
* 更多组合的结构
* 递归和自引用的结构
* 对象

1.引用与多维数组

1.1. 为什么要用引用

也许我们可以开始讨论这些结构,但是等等,我们首先要讨论的是引用的问题。
为什么呢,因为在PERL里面,不论是ARRAYS 还是 HASHES,都是单维的,因此以ARRAYS或者
HASHES为基础创建高级的数据结构,必然需要应用的引用。
引用可分为直接引用,间接引用,匿名引用三种,按照被引用的数据类型,可以是
标量引用,数组引用,HASH引用,子过程引用,句柄引用。引用如果从C的角度来看,可以看作
是指向某个已知数据的指针,对象其实也是一种引用(或者指针),它通过使用bless来绑定,我
是这么理解的。

具体的引用分类的说明我就不再过多的说了,如果有问题可以参考learning perl
book或者perlref man
手册。
好了,现在转入正题,上面说到,不论是ARRAYS 还是
HASHES,都是单维的,它的含义就是说,你在它
们的内部存放的数据都只能是标量类型的数据,包括如字符串,数值,句柄,引用。不能直接存放
其他
的数组或者HASH,这点和C的STRUCT是不太一样的。
理解了这点,对于2维或者多维数组你就有更深的理解,至少它不象表面上看到那样是多维的,它
仍然是单维的数组,存放的也不是具体的数据,而是存放的另一个匿名的单维数组的引用而已,这
样通过
引用就实现了多维数组。当然,如果你不想过多的深究,你也可以就看成多维的数组。
上篇文章的例子:
$foo[$row][$col]=’test’; #* 数组的数组
$foo{’night’}[1]=”8 Mar “; #数组的 hashes
$my_friends[$i]{’name’}=”Night Sailer”; # hashes 的数组
$lookup{’night’}{’company’}=’nightsailer’;# hashe of hashes

由于多维的数组存放的是引用,所以在使用print的时候,是不能直接打印顶层的数组的,
比如:
@a1 = ( [1, 2], [3, 4, 5], [6] );
这是一个多维的数组,我们可以
print $a1[1][1];
4
但是如果
print @a1;
ARRAY(0×84c38)ARRAY(0×8d194)ARRAY(0×8e1d0)
为什么会这样?因为@a1存放的是引用,而perl并不会隐式地反引用这些变量,
你需要自己显示的反应用这些才行。象:
${$scalar}
@{$a1[1]}
%{$hash}
或者使用->
$a1[1]->[2]
$hash->{’luck’}
等等。。。

1.2. 错误与纠正

1.2.1 上下文

在多维数组的使用中,特别需要注意的是perl的上下文,perl的上下文非常重要,给予你许多的灵
活性,
但是也带来一些不易察觉的容易凡的错误。perl会根据上下文,生成适当的变量类型,比如如果你
传递
的是一个数组变量到一个需要标量的地方,那么perl会相应的生成该数组的等同的标量变量,名字
和你
的数组变量的名字是一样的,不过它的类型是标量而不是数组,perl
允许在不同类型的变量拥有同一个
名字。同样,如果你把一个标量传递到一个需要array或者hash的地方,那么perl会生成一个同名
或者hash,我曾经不小心拼错一个数组变量,与另一个标量混了,结果在调试中死活也找不到错误
来点感性的认识吧:

for $i (1..100) {
@list = make_random_list($i)
$all_random_list[$i] = @list; # 错误!
}

这段代码的本意是以$i为初试数,用make_random_list生成随机的一个列表,然后把列表存放在
all_random_list中,但是
$all_random_list[$i] = @list;
并没有达到目的,因为此时,表达式的右边需要的是一个标量,但是我们传递给一个数组,perl于
是
根据上下文生成适当的标量类型的数据===@list中的元素个数赋值给了$all_random_list[$i]。
实际上,这里$all_random_list[$i] = @list 等同于$all_random_list[$i] = scalar @list
好,你可能会说,好的,那么我改成引用好了,象这样:
for $i (1..100) {
@list = make_random_list($i)
$all_random_list[$i] = \@list; # 错误!
}
一切OK么,不!!
太不幸,这样仍然是错误的。为什么,我们回顾一下,引用类同于指针,如果你熟悉C,你会知道�br>刚刖�
是内存的地址,是存放着为它指向的那个变量所分配的存储区域的地址!
再看一下上面的程序,@list的存储地址是固定的,因此在$all_random_list中存放的是100个相同
的存
储地址,都指向同一个@list!,本来你想保存100个随机列表,但是却只得到了一个随机列表,它的
值是你最后一次产生的。
怎么解决?
想这样:
for $i (1..100) {
@list = make_random_list($i)
$all_random_list[$i] = [@list]; # 好!
}

[@list]为LIST中的数据生成了一个匿名的数组,这样,
$all_random_list[$i]存放的就是你新产生的这
个匿名数组的引用了,你所希望的目的就达到了。[@list]为LIST中的数据生成了一个匿名的数组
$all_random_list[$i]存放的就是你新产生的这
个匿名数组的引用了,你所希望的目的就达到了。
当然,也可以通过使用my 来实现:
for $i (1..100) {
my @list = make_random_list($i)
$all_random_list[$i] = \@list; # 也可以
}
使用my以后,每次运行都会重新生成新的@list,相当于重新为@list新分配一块存储区域,而上次
分配的
存储空间由于仍有相关引用记数,perl不会释放。
不过,这种方式我个人并不推荐,没有前一种那么明朗清楚。

如果你的头脑现在还很清醒(我的脑袋有点糊涂,太晚了;=)),我们可以看看另一种形式,

for $i (1..100) {
@list = make_random_list($i)
@{$all_random_list[$i]} = @list; #你明白么?
}

和第一种方法一样么,呵呵,也许是,我也说不准,这要完全取决与$all_random_list[$i]是否de
fined
或者是否存放了另一个引用,一种情况是生成一个新的匿名的数组,另一种情况是反引用一个现存
的数组。如果之前,我们没有给$all_random_list[$i]赋值,或者虽然赋值但是已经undefined了
list的数据赋值到这个匿名数组中,和第一种方法是
一样的。不过,如果你先前已经赋值给$all_random_list[$i]一个引用:
$all_random_list[$i]=\@list_pre;
那么:
@{$all_random_list[$i]} = @list;
就会使用现存的引用,而不是重新生成一个,就是说@list_pre的数据被@list的数据覆盖了。
不管怎么说,即使可行,如果你使用这种代码书写方式,是要遭人痛骂的,除了显示你是个很特别
的家
伙以外,不容易合作以外,没有带来什么好处。

建议:
对于初学者,建议使用[]{}生成匿名的引用,不要使用\。

参考资料:http://www.baidu.com/s?wd=PERL%CA%FD%BE%DD%BD%E1%B9%B9

温馨提示:内容为网友见解,仅供参考
第1个回答  2016-01-23
下面简单介绍一些常用的赋值形式:

1)2维数组
$foo[$row][$col]=’test’;
这是典型的两维ARRAY,头一个$ROW,索引出的实际上是一个数组的引用,而后一个$COL则是对这个数组引用复原后索引到的真实数组元素的地址。
以上,数组的索引都是整数,多维数组实际上就是数组的数组,我这样认为。

2)HASH OF ARRAY
$foo{’night’}[1]=”8 Mar “;
这是一个数组的hash,每个hash的KEY 对应的值是一个数组的引用。因此,你可以用这种形式来构造一条记录,hash的key是记录的关键子,数组是的元素就是这个记录中的每个字段。

3) $my_friends[$i]{’name’}=”Night Sailer”;
$my_friends[$i]{’age’}=24;

这种是hash的数组,每个数组的元素都是一个hash的引用。同2)一样,这种结构可以用来创建记录,
与2)不同的是,字段的个数与名称都是已经确定了,当然,简单明了也是这种结构的好处。

4) $lookup{’night’}{’company’}=’nightsailer’;
$lookup{’night’}{’home adress’}=’Beijing Sanlihe road’;
这是hash of hash,前面hash的key中存的是另一个hash的引用。
这种结构是一种典型的查询表了,你可以根据名字索引到某个记录,然后根据字段名索引到相应的值。

5)$members{’linux’}{’progamming’}[0]{’name’}=’Perl’;
$members{’linux’}{’progamming’}[0]{’function’}=’gettime’;

这是一个比较复杂的结构,一般来说,记录中某个字段或元素又是引用了另外的一个结构的。
依次类推,使用array和hash就可以构造出极其复杂的数据结构。

6)$crazy->{FH}->print(&{$crazy->{myfun}}($crazy->{LIST}[0]))

这是一个很疯狂的数据结构,是吧。
这个结构首先是一个hash,hash中的每个key 分别存放着一个文件句柄,一个函数的引用,一个列表的引用,上面的代码,就是用列表中的第一个元素作为参数回调myfun函数,然后把输出结果打印到记录中指定的文件句柄,也就是打印到一个文件中去。
相似回答