Perl 哈希的创建和引用介绍

语法
创建'引用'仅有两种方法,使用它也是两种。

创建引用

创建规则 1
如果你在一个变量前加一个'/'号,你就得到了这个变量的'引用'。

$aref = /@array;         # $aref 保存着指向@array的'引用'
    $href = /%hash;          # $href 保存着指向%hash的'引用'
当你把'引用'保存在类似 $aref 或 $href的变量中,你就可以象操作其他标量一样copy或保存它。

$xy = $aref;             # $xy 现在保存了指向 @array 的'引用'
    $p[3] = $href;           # $p[3] 现在保存了指向 %hash 的'引用'
    $z = $p[3];              # $z 现在保存了指向 %hash 的'引用'
这些例子展示了如何创建命名变量的'引用',但是有时候,我们创建的数组或哈希没有名字。这个和你使用没有放到变量中去的字符串'/n'或数字'80'类似。

创建规则 2

[ ITEMS ] 创建了一个新的、匿名的数组,并返回一个指向这个数组的'引用'。 { ITEMS } 创建了一个新的、匿名的哈希,并返回那个哈希的一个'引用'。

$aref = [ 1, "foo", undef, 13 ];  
    # $aref 保存了这个数组的'引用'
    $href = { APR =>; 4, AUG =>; 8 };   
    # $href 保存了这个哈希的'引用'
从规则 2 中得到的'引用'和从规则 1 中得到的'引用'是同一种类型的:

# 这里:
        $aref = [ 1, 2, 3 ];
        # 和上面一样:
        @array = (1, 2, 3);
        $aref = /@array;
前面一种方法是后面两行的缩写,除了第一种方法没有创建一个多余的数组变量@array。

如果你只是编写符号 [], 你将得到一个新的、空匿名数组。如果你使用符号 {},就能得到一个新的、空匿名哈希。

使用引用

当你创建了一个'引用'后,你可以对它做什么操作呢?它是标量,你可以象处理任何标量一样保存和取回它。除此之外,还有两种使用方法:

使用规则 1
你可以始终用一个带有大括号的数组'引用',来替换一个数组的名字。例如,用 @{$aref} 代替 @array。

下面是一个用法的一些例子:

数组:

@a              @{$aref}                一个数组
        reverse @a      reverse @{$aref}        对一个数组做倒序排序
        $a[3]           ${$aref}[3]             数组中的一个成员
        $a[3] = 17;     ${$aref}[3] = 17        对一个成员赋值
上面每行中,两个表达式实现的是同一种功能。左边那个是对数组@a操作,右边那个是对'引用'$aref所指向的数组操作。它们对数组产生相同的作用。

使用哈希的'引用'和数组的'引用'完全一样。

%h              %{$href}              一个哈希
        keys %h         keys %{$href}         从哈希中将键取出来
        $h{'red'}       ${$href}{'red'}       哈希中的一个成员
        $h{'red'} = 17  ${$href}{'red'} = 17  对一个成员赋值
你对一个'引用'无论想做什么,使用规则 1 已经告诉你怎么做了。 你只要象使用常规的数组或哈希一样编写Perl代码,然后把数组或哈希的名字用 {$reference}来替代。‘当我只有一个'引用'时,怎么来遍历整个数组?'你这样写:

for my $element (@array) {
           ...
        }
接着用'引用'替代数组名@array:

for my $element (@{$aref}) {
           ...
        }
‘怎当我只有一个'引用'时,怎么来打印一个哈希的内容?'先写一个打印整个哈希的代码:

for my $key (keys %hash) {
          print "$key =>; $hash{$key}/n";
        }
然后用'引用'代替那个哈希的名字:

for my $key (keys %{$href}) {
          print "$key =>; ${$href}{$key}/n";
        }

使用规则 2

使用规则 1 是你真正需要的,因为它告诉了你怎么来处理一个'引用',而它对几乎任何的'引用'都有效。但是我们通常做的事情只是和数组或哈希中的一个成员有关,使用规则 1 却是很笨重的方法,所以还有简单的方法。

${$aref}[3] 太难阅读,所以我们这样写 $aref->[3]。

${$href}{red} 写的太笨重, 所以我们这样写 $href->{red}。

如果 $aref 保存的是一个数组的'引用',那么 $aref->[3] 就是这个数组的第四个成员。不要和 $aref[3] 相混淆,这个代表的是一个完全不同的数组的第四个成员,这个迷惑的数组是@aref。 变量 $aref 和 @aref 是完全不相关的,就像 $item 和 @item 一样。

同样的, $href->{'red'} 是哈希'引用' 的变量$href的一部分,甚至这是一个没有名字的哈希。而$href{'red'} 是另一个容易混淆的命名哈希 %href 的一部分。很容易忘记写上符号' ->',如果出现这种情况,当你的程序从一个你不想取数据的数组和哈希中取出了成员,你会得到奇怪的计算结果。

例子
让我们来看一个例子:

首先,记住 [1, 2, 3] 创建了一个匿名数组,包含了 (1, 2, 3),然后返回一个数组的'引用'。

现在想一下:

@a = ( [1, 2, 3],
               [4, 5, 6],
               [7, 8, 9]
             );
@a 是一个拥有三个成员的数组,每一个成员是另一个数组的'引用'。

$a[1] 是其中的一个'引用'。它指向一个数组,这个数组包含了(4, 5, 6),因为这是一个数组的'引用',使用规则 2 告诉我们可以这样写 $a[1]->[2],用来取得这个数组的第三个成员。  $a[1]->[2] 值是6。 同样的,$a[0]->[1] 值是 2。这里我们就像在使用一个二维数组;你可以是用 $a[ROW]->[COLUMN] 来取得或设置数组中任何一行任何一列中的成员。

这些符号看起来还是有些麻烦,所以还有更加简单的用法:

箭头符号规则
在两个下标之间的箭头是可选的。

我们可以用这个写法$a[1][2]来代替$a[1]->[2];它们是相同的。相对于$a[0]->[1] = 23,我们这样写$a[0][1] = 23;它们也是相同的。

现在它们看起来真的象二维数组了!

你可以发现为什么箭头这么重要。没有它们,我们必须这样写${$a[1]}[2],而不是$a[1][2]。对于三维数组,它们使我们可以简单地写成$x[2][3][5]而不是写成难读的${${$x[2]}[3]}[5]方式。

解决办法
下面是前面提出来的问题的解决方法,就是关于城市和国家名称的重新格式化。


代码如下:

my %table;
       while (<>) {
        chomp;
         my ($city, $country) = split /, /;
         $table{$country} = [] unless exists $table{$country};
         push @{$table{$country}}, $city;
       }
       foreach $country (sort keys %table) {
         print "$country: ";
        my @cities = @{$table{$country}};
        print join ', ', sort @cities;
        print "./n";
      }

这个程序分成两部分: 第 2--7 行完成数据的输入和数据结构的创建。 第 8-13 行分析这个数据并打印报告。我们设置了一个哈希 %table,它的键是国家名称,它的健值是这个国家名称对应的城市名的数组的'引用'。这个数据结构看起来如下:

%table
        +-------+---+   
        |       |   |   +-----------+--------+
        |Germany| *---->| Frankfurt | Berlin |
        |       |   |   +-----------+--------+
        +-------+---+
        |       |   |   +----------+
        |Finland| *---->| Helsinki |
        |       |   |   +----------+
        +-------+---+
        |       |   |   +---------+------------+----------+
        |  USA  | *---->| Chicago | Washington | New York |
        |       |   |   +---------+------------+----------+
        +-------+---+
我们先来分析输出部分。假设我们已经拥有了这个结构,那么我们怎么来输出呢?


代码如下:

foreach $country (sort keys %table) {
         print "$country: ";
        my @cities = @{$table{$country}};
        print join ', ', sort @cities;
        print "./n";
      }

%table是一个普通的哈希,我们从它这里可以取得一列键,对键进行排序,并遍历所有的键。这里唯一使用'引用'的是第10行。$table{$country} 查看了哈希中的键$country并取得它的值。这个健值是对应国家中的城市数组的'引用'。 使用规则 1 告诉我们可以通过使用 @{$table{$country}}来恢复整个数组。第10行就象

@cities = @array;
不同的是这里的数组的名字被'引用' {$table{$country}}所替代。符号 @ 告诉Perl去获取整个数组。得到了城市的列表后,我们照样对其进行排序,合并城市名,并打印出来。

第2-7行负责创建数据结构,如下:


代码如下:

while (<>) {
        chomp;
         my ($city, $country) = split /, /;
         $table{$country} = [] unless exists $table{$country};
         push @{$table{$country}}, $city;
       }

第 2-4 行获取城市和国家的名称。第5行查看这个国家名称是不是已经作为一个键存放在哈希里面了,如果没有,程序就使用符号[] (创建规则 2)创建一个新的、空的匿名数组,同时把一个指向这个匿名数组的'引用'作为健值放到哈希里面去了。

第6行将城市名放到对应的数组里面。$table{$country} 现在保存了一个'引用',它指向所对应的国家的城市的数组。第6行就象

push @array, $city;
不同的是这里的数组名被{$table{$country}}所替代。 命令 push 将城市名加到这个'引用'指向数组的最后。

这里有一个要点被我忽略了。第5行是不需要的。我们可以取掉它。


代码如下:

while (<>) {
        chomp;
         my ($city, $country) = split /, /;
       ####  $table{$country} = [] unless exists $table{$country};
         push @{$table{$country}}, $city;
       }

如果在哈希 %table 中已经有这个国家名 $country的记录,那么,加不加第5行没有任何区别。第6行会自己定位到$table{$country}这个'引用'指向的数组,把值 $city 放到数组中去。但是如果在%table中没有那个键,比如Greece,那么它会怎么办呢?

这是Perl,它会自己准确地完成工作。你想把一个Athens赋值给一个不存在地数组,那么Perl会帮助你创建一个新的、空的匿名数组,将它放到哈希%table里面去,然后把值 Athens 放到这个数组中。这个被称为'自动生成' --让事物自己自动产生出来。 Perl 发现在哈希里面没有这个键,就自动地创建了一个新的哈希记录。 Perl 发现你想要使用数组作为哈希的健值,它就自动创建一个匿名的空数组,并将指向这个数组的'引用'放到那个哈希中去。一般, Perl 创建的数组只有一个成员大小,用于保存这个新的城市名。

其他集锦
我承诺以10%的细节来使你得到90%的好处,那就意味着我跳过了90%的知识的细节。现在来看一下其中的重要的部分,这个比阅读手册 the perlref manpage 要容易得多,手册讨论了100%的细节。

手册 the perlref manpage中的一些集锦:

你可以对任何东西创建'引用',包括标量,函数和其他的引用。

在 使用规则 1 中,当大括号里面是一个象$aref这样的标量变量时,你可以省略掉这个大括号。例如, @$aref 和 @{$aref}是一样的,$$aref[1] 和 ${$aref}[1]是一样的。 如果你是初学者,建议你还是养成加上大括号的习惯。

下面的操作不会copy '引用'指向的数组: 
        $aref2 = $aref1;
你将得到两个'引用',它们都指向同一个数组。如果你修改了$aref1->[23]的值,那么你查看变量$aref2->[23]时,它也相应地变了。

要copy这个数组,你需要这样

$aref2 = [@{$aref1}];
使用符号 [...] 来创建一个新的匿名数组, 而且这个新的数组的'引用'被赋值给了$aref2 。 这个新的数组用'引用'$aref1所指向的数组的内容来初始化。

同样的,要copy一个匿名哈希,你需要这样

$href2 = {%{$href1}};

如果要判断一个变量保存的内容是不是'引用',使用函数ref 。如果它的参数是'引用',返回的值是'真'。实际上,它做得更好:如果是一个哈希的引用,它返回'HASH',如果是一个数组的引用,那么就返回'ARRAY'。

如果你想像字符串一样使用'引用'的话,你得到的字符串就像 
        ARRAY(0x80f5dec)   or    HASH(0x826afc0)
如果看到一个像这样的字符串,你应该知道你错误地输出了一个'引用'。

这种显示方式的另一个作用是你可以用eq来比较两个'引用',看它们是不是指向相同的东西。(你通常可以使用 == 来比较,因为它更会)

你可以像使用'引用'一样来使用一个字符串。如果你使用"foo"作为一个数组的'引用',它就是指向数组 @foo的一个引用。这被称为'软引用'或‘符号引用 '。 使用申明 use strict 'refs' 可以取消这个功能,如果你不小心使用了它,会导致各种可能的错误。

你可能更喜欢查看 the perllol manpage,而不是手册 the perlref manpage;它详细地讨论了列表的列表和多纬数组。然后,你可以继续学习手册 the perldsc manpage;它是数据结构的Cookbook, 它提供了处理哈希的数组,数组的哈希,以及其他数据结构的方法。

(0)

相关推荐

  • Perl哈希表用法解析

    本文和大家重点讨论一下Perl哈希表的概念,Perl语言和其他编程语言各有各的特点,这里和大家分享一下Perl哈希表的概念,其实Perl哈希表是一种结构. Perl哈希表Perl哈希表是一种结构.key/value.访问Perl哈希表元素$Perl哈希表{$some_key}当给Perl哈希表选择名字时,最好这样思考:Perl哈希表元素的名字和key之间可以用for来连接.如thefamily_nameforfredisflintstone. 要引用整个Perl哈希表,使用百分号(%)作为前缀.

  • php-perl哈希算法实现(times33哈希算法)

    复制代码 代码如下: APR_DECLARE_NONSTD(unsigned int) apr_hashfunc_default(const char *char_key,                                                      apr_ssize_t *klen){    unsigned int hash = 0;    const unsigned char *key = (const unsigned char *)char_key;  

  • perl哈希hash的常见用法介绍

    基本用法 #初始化%h为空数组 %h={};#用数组初始化%h为a=>1,b=>2  %h=('a',1,'b',2);#意义同上,只是另一种更形象化的写法.%h=('a'=>1,'b'=>2);#如果key是字符串,可以省略引号.下面这行和上面那行是一样的%h=(a=>1,b=>2);#用{}来访问print "$h{a}\n";#打印 $h{b}='2b'; print "$h{b}\n"; #打印2b#删除key用delet

  • perl哈希的一个实例分析

    复制代码 代码如下: #!/bin/perluse strict;  use warnings; my %movies;  my $film;  my %reverse_result;  my $director;  my @data; %movies =  (    'The Shining'       => 'Kubrick',    'Ten Commandments'  => 'DeMille',    'Goonies'           => 'Spielberg', 

  • Perl 哈希Hash用法之入门教程

    一.什么是Perl Hash 哈希是一种数据结构,和数组类似,可以将值存放到其中,或者从中取回值.但是,和数组不同的是,其索引不是数字,而是名字.也就是说,索引(这里,我们将它叫key)不是数字而是任意的唯一的字符串. key可以是任意的字符串,你可以使用任何的字符串作为key,但它们是唯一的.另一种思考hash 的方法是,把它看作一堆数据(a barrel of data),每一个数据都有一个相应的标签.可以通过标签访问此标签对应的元素.但其中是没有"第一个"元素的概念的.在数组中,

  • Perl与JS的对比分析(数组、哈希)

    上一篇列出了Perl中定义数组,对象的方式与JS的异同.这里继续补充数组,哈希的相关操作. 一.数组 可以对数组进行增删,插入.与JS不同的是这些函数都是全局的,JS则是挂在Array.prototype上. 1,对数组尾部的操作pop(删除最后的元素).push(在尾部添加) @goods = qw/pen pencil/; pop(@goods); # @goods 变成 (pen) push(@goods, 'brush'); # @goods 变为 (pen, brush) 在Perl中

  • Perl 哈希的创建和引用介绍

    语法 创建'引用'仅有两种方法,使用它也是两种. 创建引用 创建规则 1 如果你在一个变量前加一个'/'号,你就得到了这个变量的'引用'. $aref = /@array;         # $aref 保存着指向@array的'引用'     $href = /%hash;          # $href 保存着指向%hash的'引用' 当你把'引用'保存在类似 $aref 或 $href的变量中,你就可以象操作其他标量一样copy或保存它. $xy = $aref;           

  • Java中弱引用和软引用的区别以及虚引用和强引用介绍

    知道弱引用和软引用的概念与如何使用它们是两码事,引用类在垃圾回收工作的过程中有重要作用.我们都知道垃圾回收器会回收符合回收条件的对象的内存,但并不是所有的程序员都知道回收条件取决于指向该对象的引用类型.这正是Java中弱引用和软引用的主要区别.如果一个对象只有弱引用指向它,垃圾回收器会立即回收该对象,这是一种急切回收方式.相对的,如果有软引用指向这些对象,则只有在JVM需要内存时才回收这些对象.弱引用和软引用的特殊行为使得它们在某些情况下非常有用.例如:软引用可以很好的用来实现缓存,当JVM需要

  • 对python创建及引用动态变量名的示例讲解

    实际上在python中用列表就可以实现动态变量名的管理,python中的列表中可以存储任何类型的元素: listA = [0,"str",B()] 上述列表分别存储了整数,字符串,对象.使用和创建时只需配合列表下标即可. 但python确实有创建动态表量名的方法: names = locals() for i in range(1,10): names['a%i'%i] = input('Abss %d'%i) for i in range(1,10): print(names['a%

  • C语言函数栈帧的创建和销毁介绍

    在初学c语言中,很多时候要记的内容有点多,有时候并不能深入的了解它.关于函数的栈帧可以帮助我们深入了解函数传参的过程,让我们了解c语言. 以下是我们平时接触过,但不了解的问题: 1.为什么局部变量在未赋值前是随机的. 2.局部变量创建的过程. 3.函数传参,传参的顺序问题. 4.形参与实参的关系什么. 5.调用函数是怎么调用的,调用的过程是什么. 6.调用函数结束后,是怎样返回的. 这些问题我们在学校可能并不会接触,也不会出现在考试的试卷上,但是作为计算机专业的学生,做一些认识和了解是很有必要的

  • perl中my与our的区别介绍

    先来看下our的用法. require 5.006当版本号小于 5.006 的时候,会返回失败,从而导致模块加载失败.所以它的作用就是保证模块调用环境的 Perl 版本. our 和 my 一样,都是对变量的声明,不过 our 声明的是包全局变量,而 my 声明的是词法变量. 不过,经过 our 声明的变量,它会变得像一个词法变量一样,其实这也是 our 存在的目的:用来欺骗 strict pragma,使 strict 以为它是一个词法变量,其实却不是. 有一个简单的办法可以理解 our:1,

  • C++标准之(ravalue reference) 右值引用介绍

    1.右值引用引入的背景 临时对象的产生和拷贝所带来的效率折损,一直是C++所为人诟病的问题.但是C++标准允许编译器对于临时对象的产生具有完全的自由度,从而发展出了CopyElision.RVO(包括NRVO)等编译器优化技术,它们可以防止某些情况下临时对象产生和拷贝.下面简单地介绍一下CopyElision.RVO,对此不感兴趣的可以直接跳过: (1)CopyElision CopyElision技术是为了防止某些不必要的临时对象产生和拷贝,例如: 复制代码 代码如下: structA{ A(

随机推荐

其他