PERL编程24学时教程——13 引用与结构.pdf
-
资源ID:70008656
资源大小:487.62KB
全文页数:12页
- 资源格式: PDF
下载积分:15金币
快捷下载
会员登录下载
微信登录下载
三方登录下载:
微信扫一扫登录
友情提示
2、PDF文件下载后,可能会被浏览器默认打开,此种情况可以点击浏览器菜单,保存网页到桌面,就可以正常下载了。
3、本站不支持迅雷下载,请使用电脑自带的IE浏览器,或者360浏览器、谷歌浏览器下载即可。
4、本站资源下载后的文档和图纸-无水印,预览文档经过压缩,下载后原文更清晰。
5、试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。
|
PERL编程24学时教程——13 引用与结构.pdf
下载第1 3学时引用与结构如果P e r l是你使用的第一个编程语言,那么本学时将会使你感到颇有兴趣。在大多数编程语言中,你会发现一个概念,即一组数据实际上可以是对另一组数据的引用。有时这些引用称为指针(在p a s c a l或C语言中),有时这种技术称为间接引用(在汇编语言中),而有些语言则根本没有指针的概念(在 B A S I C或J a v a中)。如果你以前从未使用过引用、指针或间接引用等概念,那么可能必须多次阅读本学时讲解的某些部分的内容,否则会感到混淆不清。P e r l也拥有这些特殊类型的值,不过在 P e r l中,它们都称为引用。在 P e r l中,引用可以用于许多目的,但在本学时中,你要学习的是如何使用引用来调用带有多个参数的复杂函数和如何创建复杂的数据类型,如列表的列表。所谓引用,它非常类似老式图书馆中的卡片目录。目录中的每个索引卡指的是图书馆中的一本书。卡片可以指明这本书是什么类型的书(比如小说、非小说、参考书等),并指明这本书放在什么位置。有些卡片目录可能配有对同一本书的若干个引用,它们是不同种类的引用,并且甚至可以参见该目录中的其他卡片。P e r l的引用类似卡片目录,可以指向各组数据。引用能够知道它指向的是何种类别的数据(如标量、数组或哈希),也知道这些数据在什么地方。引用可以被拷贝,但不改变原始数据的任何东西。对于同一组数据,可以进行多次引用。实际上一个引用可以指向其他的引用。请牢记下面这些要点,慢慢阅读下面几页内容,并且在我们介绍有关的问题时保持清醒的头脑:引用的基本概念。引用的常见结构。运用所有这些概念而建立的一个简要代码例子。13.1 引用的基本概念使用赋值运算符,可以创建和赋值一个普通的标量变量,如下所示:在这个代码段建立后,可以创建一个称为$a的标量变量,它包含字符串“S t o n e s”。到现在为止,一切都很正常。这时,在计算机中的某个地方有一个标为$a的位置,它包含了该字符串,如下图所示:如果将标量$b赋予$a,比如$a=$b,那么会产生该数据的两个拷贝,它们使用两个不同的名字,如下图所示:如果你想要两个独立的数据拷贝,那么拥有两个拷贝是很好的。但是,如果想让$a和$b都引用同一组数据,而不是引用一个数据拷贝,那么必须创建一个引用。所谓引用,它只是指向一组数据的指针,并不包含实际数据的本身。该引用通常存放在另一个标量变量中。若要创建对某个既定变量的引用,可以在该变量的前面加上一个反斜杠。例如,若要创建称为$r e f的对$a的引用,只需要像下面这样将引用赋予$r e f即可:这个赋值创建了类似下面这样的条件:$r e f并不包含用于它自己的任何数据,它只是对$a的一个引用。变量$a根本没有改变,它仍然可以照常被赋值($a=“F o o”)或显示(print$a)。变量$r e f现在包含对$a的引用。不能简单地对$r e f进行操作,因为它里边没有通常的标量值。实际上,如果输出$r e f,就会显示类似S C A L A R(0 x 0 0 0 0)的信息。若要通过$r e f获得$a中的值,必须间接引用$r e f。间接引用可以被视为上面的方块图中按箭头方向的引用。若要通过引用$r e f来输出$a的值,你可以像下面这样使用另一个$:在上面的代码段中,$r e f当然包含了引用。增加的一个$告诉P e r l,$r e f中的引用指的是一个标量值。$r e f引用的标量值被取出并输出。也可以通过引用来修改原始值,这是你对数据拷贝所不能进行的操作。下面这个代码用于修改$a中的原始值:这项修改形成了类似下面这样的引用关系:如果你使用$r e f而不是$r e f那么存放在$r e f中的引用将被撤消并被实际值取代,如下所示:上面这个代码段运行后,$r e f不再包含一个引用,它只是一个标量。你可以像任何其他标量值那样,给引用赋值:得到的结果如下:上面的代码段运行后,$o r e f和$n r e f均可用于获取值“G a n d a l f”。也可以存放对某个引用的引用,如下所示:148使用第二部分高 级 特 性下载在这个例子中,引用链接类似下面的形式:如果使用$b r e f 2来输出书名,那么该引用将是$b r e f 2,如果使用$b r e f,则该引用是$b r e f。请注意,$b r e f 2多了一个美元符号,它需要增加一层间接引用,才能获得原始值。13.1.1 对数组的引用也可以创建对数组和哈希结构的引用。可以像创建对标量的引用那样,使用反斜杠来创建对数组和哈希结构的引用:现在标量变量$a r e f包含了对整个数组 a r r的引用。直观地说,它类似下面的形式:若要使用引用$a r e f来访问 a r r的各个部分,你可以使用下列代码之一:$a r e f 0 a r r的第一个元素$a r e f 2,3 a r r的一个片$a r e f a r r的整个数组为了清楚起见,可以使用花括号将引用与涉及数组的各个部分隔开,如下所示:$a r e f 0 与$a r e f 0 相同$a r e f 2,3 与$a r e f 2,3 相同$a r e f与$a r e f 相同例如,若要使用数组引用$a r e f,以便输出 a r r的所有元素,可以使用下面这个代码:13.1.2 对哈希结构的引用若要创建对哈希结构的引用,可以使用反斜杠,就像创建标量和数组的引用那样:上面这个代码段用于创建对哈希结构%h a s h的引用,并将它存放在$h r e f中。这个代码段创建的引用结构类似下面的形式:若要使用对哈希的引用%h r e f来访问%h a s h的各个部分,可以使用下面这些代码例子:$h r e f k e y 访问%h a s h中的一个关键字,也可以是$h r e f k e y%$h r e f访问整个哈希结构,也可以是%$h r e f 若要迭代通过该哈希结构,输出所有的值,可以使用下面这个代码:第13学时 引用与结构使用149关键字数据数据数据数据关键字 关键字 关键字数据 数据 数据 数据 数据下载13.1.3 作为参数的引用由于整个数组或哈希结构均可被引用,并且该引用可以存放在一个标量中,因此,借助这些引用,你可以调用带有多个数组或哈希结构的函数。你可能还记得第8学时中我们讲过,下面这种代码段是不能运行的:这个代码不能运行,因为 g e t a r r a y s(f r u i t,v e g g i e s)将两个数组压缩到单个数组 _中。在g e t a r r a y s()函数中,将 a和 b赋予 _,会导致现在存放在 _中的 f r u i t s和 v e g e t a b l e s的所有元素都被赋予 a。当所有数组挤入 _之后,就没有办法知道一个数组在何时结束和下一个数组在何时开始。只有一个很大的统一的列表。这就是引用可以发挥作用的地方。你不必将整个数组传递给 g e t a r r a y s,只要传递对这些数组的引用,就能够很好地达到你的目的:函数g e t a r r a y s()总是接收两个值,即两个引用,无论这些引用指向的数组有多长。这时,$f r u i t _ r e f和$v e g _ r e f可以用来显示或编辑数据,如下所示:当你将对标量、数组或哈希结构的引用作为参数传递给函数时,有几个问题必须记住。当你传递引用时,函数能够对引用指向的原始数据进行操作。请看下面这些例子:150使用第二部分高 级 特 性下载在左边的例子中,当按正常情况传递哈希结构时,_取得原始哈希结构%h a s h中每个关键字值对的各个值。在子例程 c h a n g e h a s h()中,现在放入 _中的哈希结构的各个元素被拷贝到称为%l o c a l _ h a s h的新哈希结构中。哈希%l o c a l _ h a s h被修改,该子例程返回。当子例程返回后,%l o c a l _ h a s h就被撤消,而程序的主要部分中的%h a s h则保持不变。在右边这个例子中,对%h a s h的引用通过 _被传递到子例程c h a n g e h a s h()中。该引用被拷贝到标量$h r e f中,它仍然指原始哈希%h a s h。在子例程中,$h r e f指向的哈希结构被修改,子例程返回。c h a n g e h a s h()返回后,原始哈希结构%h a s h将包含新关键字b e a r。当数组 _用于传递子例程参数时,它是个引用的数组。修改 _数组的元素就会改变传递到函数中的原始值。修改传递给子例程的参数,通常被认为是不慎重的一种做法。如果你想让子例程修改传递给它们的参数,那么应该传递对子例程的引用。这种操作方法更加清楚。当传递一个引用时,可以认为原始值是可以修改的。13.1.4 创建各种结构创建对数组和哈希结构的引用,可以用来与子例程之间来回传递这些结构,并且可以用来创建下面我们很快就要介绍的一些复杂结构。不过你应该知道,当你创建了对哈希结构或数组的引用后,就不再需要原始哈希结构或数组。只要对哈希结构或数组的引用存在,即使原始数据不再存在,P e r l仍然保留着哈希结构和数组的各个元素。在下面的代码段中,代码块中创建了一个哈希结构%h a s h,并且这个哈希结构是该代码块的专用结构:在这个代码块中,标量$h r e f被赋予对%h a s h的引用。当该代码块存在时,即使%h a s h已经消失,$h r e f中的引用仍然有效(因为%h a s h是代码块的专用结构)。当结构本身已经超出作用域之后,对该结构的引用仍然可以存在,$h r e f引用的哈希结构仍然可以修改。如果你观察上面这个代码块,就会发现,它的唯一目的是创建对哈希结构的引用。P e r l提供了一个机制,可以用来创建这样的引用,而不必使用中间的哈希结构%h a s h。这个机制称为匿名存储。下面这个例子创建了一个对匿名哈希结构的引用,并把它存储在$a h r e f中:花括号()将哈希结构括起来,返回对它的引用,但实际上并没有创建新的变量。你第13学时 引用与结构使用151下载可以使用前面的“对哈希结构的引用”这一节中介绍的所有方法,对匿名哈希结构进行操作。也可以使用方括号()创建匿名数组。同样,也可以使用前面的“对数组的引用”这一节中介绍的方法对数组的引用进行操作。当引用的变量本身超出作用域时(如果它是个专用变量),那么该引用指向的数据将全部消失,如下所示:如果use strict正在运行,那么上面这个代码段甚至不进行编译。P e r l将$r e f的最后一个实例视为全局变量,这是不允许的。即使没有use strict,P e r l的-w警告特性也会输出一个undefined value(未定义的值)消息。这些匿名哈希结构和匿名数组可以组合成某些结构形式,我们将在下一节中介绍这些结构。每个哈希结构和数组的引用代表一个标量值,并且由于它是单个标量值,因此可以存放在其他数组和哈希结构中,如下所示:13.2 结构的配置方法下面各节将介绍列表和哈希结构的一些常用结构配置方法。13.2.1 一个例子:列表中的列表在P e r l中,列表中的列表常常用来代表一种称为二维数组的结构。也就是说,标准数组是个值的线性列表,如下所示:二维数组类似一个值的表格,里面的每个元素按照轴上的一个点来进行编址。索引的第一部分表示行号(从0开始),第二部分是列号,请看下图:152使用第二部分高 级 特 性下载值1数据数据数据数据数据数据数据数据数据值2值3值4P e r l实际上并不支持真正的二维数组。P e r l允许你使用数组引用的数组,模仿建立二维数组。若要创建数组的数组,请使用下面这个原义表达式:请认真观察上面的代码段。它创建了一个正则列表 l i s t _ o f _ l i s t s,但是它由对其他列表的引用所组成。若要访问最里层的列表的各个元素(即二维数组中的单元格),可以使用下面这个代码:若要确定最外层的列表中的元素数目,你可以像对其他任何数组那样进行操作,使用$#表示法或者使用标量上下文中的数组名:若要确定里层列表中的某个列表的元素数目,可能有一点儿麻烦。语句$l i s t _ o f _ l i s t s 1 返回 l i s t _ o f _ l i s t s的第二行中的引用。如果将它输出,则显示类似A R R AY(0 x 0 0 0 0 0)这个数据。若要将 l i s t _ o f _ l i s t s的一个元素当作数组来处理,请在它的前面加上一个符号,如下所示:若要遍历列表的列表中的每个元素,可以使用下面这个代码:可以添加下面这样的结构:13.2.2 其他结构在上一节中,我们介绍了如何使用引用和数组创建基本的 P e r l结构,即列表的列表。实际上可以将数量不受限制的数组、标量和哈希结构的变形组合起来,创建更为复杂的数据结构,比如下面这些结构:哈希结构的列表。列表的哈希结构。哈希结构的哈希结构。包含列表的哈希结构,而列表中又包含哈希结构,等等。由于本书篇幅有限,无法一一介绍所有这些结构。你安装的每个 P e r l所配备的在线文档包含了一个称为“Perl Data Structures Cookbook(Perl的数据结构大全)”文档。它详细而明白地描述了这些结构和许多其他数据结构。对于每种数据结构,“Perl Data Structures Cookbook”文档详细描述了下列信息:第13学时 引用与结构使用153下载 说明你的结构(原义表示法)。填充你的结构。添加各个元素。访问各个元素。遍历整个数据结构。若要查看“Perl Data Structures Cookbook”,请在命令提示符处键入perldoc perldsc。13.2.3 使用引用来调试程序当使用引用对程序进行调试时,编程新手常常搞不清楚哪些引用指向什么种类的数据结构。另外,在你习惯之前,语句也容易混淆。P e r l提供了一些工具,可以帮助你确定有关的情况。首先,可以输出该引用。P e r l能够显示该引用指向什么结构。例如,下面这个代码行:可以显示这个结构意味着变量$m y s t e r y _ r e f e r e n c e是对一个数组的引用。此外,变量也可以是对标量(S C A L A R)、哈希结构(H A S H)或子例程(C O D E)的引用。若要输出$m y s t e r y _r e f e r e n c e指向的数组,可以将它作为数组来处理,如下所示:P e r l的调试程序也配有一些程序工具,帮助你确定某个引用指向什么数据结构。在调试程序中,你可以像通常那样输出引用。下面这个代码段显示了一个被查看的名叫$r e f的引用:显然,$r e f是指一个哈希结构。该调试程序包括一个命令,即命令 x,它将输出该引用和它的内部结构:在这个代码中,该引用包含一个带有两个元素(关键字 f r u i t和v e g e t a b l e)的哈希结构。该调试程序甚至能够输出列表的列表之类的复杂数据结构,如下所示:上面的例子显示了一个引用$a,它指向一个数组 A R R AY(0 x 2 0 1 7 0 b d 4)。而这个数组又包含 3个别的数据引用,即 A R R AY(0 x 2 0 11 5 4 8 4)、A R R AY(0 x 2 0 11 f b b 4)和 A R R AY154使用第二部分高 级 特 性下载(0 x 2 0 11 f a a 0),每个数组包含3个元素。模块D a t a:D u m p e r包含的一些函数能够显示各个引用的内容。D a t a:D u m p e r是独一无二的,它的输出格式是有效的 P e r l格式,它可以存入文件,并在以后被检索,以提供可存储的结构。D a t a:D u m p e r模块将在第1 4学时中介绍。13.3 练习:另一个游戏迷宫当你学习了那么多的新奇概念(引用和结构)之后,需要来一点消遣娱乐了。下面这个练习展示了一种结构和几个引用,并且你可以做一个简单的游戏。采用探险和狩猎之类的传统游戏方式,你被置于一个迷宫之中,必须找到你的出路。这个迷宫并无奇特之处,它只是由一些房间所组成,并且每个房间至少有一个门。门可以通向位于东、南、西、北的相邻房间。这个游戏的目的是找到一间密室。你会发现通往该密室只有两条路,另外还有许多走不通的路。首先,键入程序清单 1 3-2,并将它保存为M a z e。运行该程序,得到类似程序清单 1 3-1的输出。程序清单13-1 Maze的输出示例程序清单13-2 Maze的完整程序清单第13学时 引用与结构使用155下载第1 2行:这两行代码是P e r l程序正常的开始。-w使警告特性被激活,use strict用于捕获错误和不恰当的编程做法。第4 9行:用于定义描述迷宫 m a z e的结构。显示的迷宫是个44的栅格,用一个列表的列表来表示。列表的每个元素用于描述迷宫中的任何一个房间可以通往哪些房间,因此,如果你重新设计这个迷宫,请务必留出一条出路。当前的迷宫如下所示:有一个房间(2,1)是无法进入的,在这个结构中用一个-来表示这个房间。实际上,不能与n、s、e或w匹配的任何字符串均可使用。第1 0 11行:当游戏的玩主向北或向南移动时,迷宫中的当前位置就需变更。哈希结构%d i r e c t i o n用来根据老的位置和移动方向计算玩主的新位置。如果向“北”移动,则使玩主的x坐标移动-1(向上),y坐标保持不变。如果向“东”移动,则玩主的 x坐标不变,而y坐标增加1。你将在第3 3 3 4行代码中看到坐标的变更情况。第1 3 1 5行:程序中使用的变量用 m y进行声明,以便使 use strict恰当地运行。存放在$c u r r _ x和$c u r r _ y中的玩主当前位置被设置为0,0。最后目的地$x和$y被设置为3,3。第1 7行:根据栅格中的x,y坐标,该函数显示玩主可以在每个房间中移动的方向。第2 0行:在$m a z e$c x$c y 的房间描述中选择字母n、s、e和w,每次选择1个字母。从哈希结构%f u l l中显示n s e w方向的相应描述。这个哈希结构只用于将短名字(n)转换成长名字156使用第二部分高 级 特 性下载入口目标(N o r f h),供显示之用。第2 5行:该函数取出一个方向(存放在$n e w中)和对玩主的当前位置的引用。第2 8行:方向用l c改为小写字母,s u b s t r只取出第一个字母,并将它赋予$n e w。这样,E a s t变为e,We s t变为w,s仍为s。第2 9行:搜索当前房间的$m a z e$x r e f$y r e f,找出给定的方向(n、s、e和w)。如果不存在给定的方向,那么它对该房间无效,然后输出一条消息。第3 3 3 4行:玩主的x和y坐标被更改。如果方向是e,则$d i r e c t i o n e 是对两个元素的数组的引用(0,1)。x坐标将递增0,即$d i r e c t i o n e 0。Y坐标将递增1,即$d i r e c t i o n e 1。第3 7行:程序的主体从这里启动运行。该循环将不断运行,直到玩主的x和y坐标($c u r r _ x,$c u r r _ y)与密室的坐标($x,$y)相一致为止。第3 8行:显示当前房间的“映像”。第3 9行:需要的移动方向读入$m o v e,用c h o m p删除换行符。如果玩主键入以 q开头的任何信息,则游戏结束。第4 2行:根据玩主当前需要做的移动和对玩主坐标的引用,调用子例程m o v e _ t o()。m o v e _ t o()子例程通过调整$c u r r _ x和$c u r r _ y,使玩主作相应的移动。若要修改迷宫,使之采用另一种布局,只需改变存放在 m a z e中的栅格。迷宫不一定需要做成正方形,也不需要给每个房间制作映像,甚至不需要存在一条有效的路径。不过请记住,迷宫不要从它边上的某个房间开始。程序不会检查迷宫的有效性,不过,如果你创建了一个无效迷宫,P e r l就会发出警告。如果要移动迷宫中的密室,只需改变它的$x和$y的值。13.4 课时小结本学时我们介绍了引用的基本概念。首先,讲述了如何创建对 P e r l的基本数据结构标量、数组和哈希结构的引用。然后,介绍了如何使用这些引用,对原始数据结构进行操作。接着,说明了如何创建对哈希结构或数组的引用,不过这种引用没有与此相关的变量名,这种引用称为匿名存储。最后介绍了如何使用引用来创建复杂的数据结构,以及何处可以查找已有数据结构的文档资料。13.5 课外作业13.5.1 专家答疑问题:当我用p r i n t“L O L”输出一个列表的列表时,它输出的是 A R R AY(0 x 1 0 1 2 1 0),A R R AY(0 x 1 0 1 4 0 0)等等,为什么?解答:对于正规数组来说,p r i n t“a r r a y”将输出数组的元素,各个元素之间有一个空格。P r i n t“L O L”也产生这样的结果,输出 L O L中的各个数组元素。若要输出 L O L中每个数组的组件,你必须使用本学时开头的“一个例子:列表中的列表”这一节中介绍的方法。问题:我试图使用$r e f=($a,$b,$c)创建一个对列表的引用,结果却产生了一个对标量值而不是列表的引用,为什么?解答:在P e r l中,($a,$b,$c)实际上是($a,$b,$c)的简化形式。你得到的结果实际上是对括号中最后一个元素$c的引用。若要获得一个对匿名数组的引用,你应该使用第13学时 引用与结构使用157下载$r e f=$a,$b,$c。13.5.2 思考题1)语句$r e f=“p e a n u t s”;运行后,$r e f中包含了什么?a.什么也不包含。该语句无效。b.peanuts。c.对一个匿名标量的引用。2)下面这个结构可以创建什么?a.一个哈希结构的哈希结构,它包含一个列表b.一个哈希结构的列表,它包含一个列表c.一个列表的列表,它包含另一个列表13.5.3 解答1)答案是c。你可以创建对任何值的引用,而不只是创建对标量、数组和哈希变量的引用。你也可以用$r e f=1 0 0;创建对一个数字的引用。如果你的答案是 a,那么最好在一个短程序或调试程序中试用一些新数据,看看它们能够产生什么结果。2)答案是b。在本学时中我们没有具体介绍这种结构,不过你应该能够猜到这是个什么结构。一个哈希结构(花括号中)的列表(外层方括号)包含一个列表(k i d s的数据)。13.5.4 实习 修改M a z e游戏,使之也能按对角线方向移动。你可以使用 4个新关键字来表示这些方向(n e、n w、s e和s w是很难编程的)。提示:关键是修改 m a z e时使用新符号和%d i r e c t i o n来表示按什么方向移动,比如 1,1 -1,-1 等。设计一种结构(即使是纸上谈兵也行),来描述一种电话帐单。帐单本身包含类似哈希结构的关键字和数据(名字,电话号码,地址),帐单的某些部分是列表(明细电话项)。每个明细电话项也可以被视为一个哈希结构(电话接收方,时间)。158使用第二部分高 级 特 性下载