在大约2小时30分钟内学习Perl

Perl是一种动态、动态类型、高级脚本(解释)语言,与PHP和Python最具可比性。Perl的语法在很大程度上要归功于古老的shell脚本工具,它以过度使用令人困惑的符号而闻名,其中大多数符号是谷歌无法找到的。Perl的shell脚本传统使其非常适合编写胶水代码:链接其他脚本和程序的脚本。Perl非常适合处理文本数据和生成更多的文本数据。Perl广泛、流行、高度可移植且支持良好。Perl的设计理念是“There’s More One Way To Do It”(TMTOWTDI)(与Python相反,Python's“should be One,preference only One the objective Way To Do It”)。

Perl很可怕,但它也有一些很好的弥补功能。在这方面,它就像所有其他创建的编程语言一样。

本文件旨在提供信息,而非福音。它针对的是像我这样的人:

  • 不喜欢上的官方Perl文档http://perl.org/因为技术性很强,给了非常不寻常的边缘案例太多的空间
  • 通过“公理与范例”最快速地学习新的编程语言
  • 希望拉里·沃尔能说到点子上
  • 已经知道如何进行一般编程
  • 除了完成工作所需的内容之外,不关心Perl。

本文件旨在尽可能简短,但不得更短。

初步说明

  • 对于本文件中的几乎每一个声明都可以这样说:“严格来说,这不是真的;情况实际上要复杂得多”。如果你看到一个严重的谎言,请指出,但我保留保留对儿童的某些关键谎言的权利。

  • 在本文档中,我使用了示例打印语句输出数据,但不显式附加换行符。这样做是为了防止我发疯,并更加注意每种情况下打印的实际字符串,这总是更重要。在许多示例中,如果代码实际运行,这将导致多个单词全部混在一行中。试着忽略这一点。

你好,世界

一个Perl脚本是一个扩展名为.pl文件.

这是的全文helloworld.pl(你好世界.pl):

使用严格;使用警告;打印“你好,世界”;

Perl脚本由Perl解释器解释,珍珠perl.exe文件:

perl helloworld.pl[arg0[arg1[arg2…]]]

一些即时的注意事项。Perl的语法是高度宽松的,它允许您做一些事情,这些事情会导致具有不可预测行为的看起来模棱两可的语句。我没必要解释这些行为是什么,因为你想避免它们。避免它们的方法是使用严格;使用警告;在您创建的每个Perl脚本或模块的最顶层。表格声明使用foo;杂注。杂注是指向perl.exe文件,在程序开始运行之前执行初始语法验证时生效。当解释器在运行时遇到这些行时,它们不起作用。

分号,;,是语句终止符。符号#开始注释。注释持续到行末。Perl没有块注释语法。

变量

Perl变量有三种类型:标量,阵列哈希每种类型都有自己的符号:$,@%分别是。变量声明使用我的,并保留在范围中,直到封闭块或文件结束。

标量变量

标量变量可以包含:

  • 未定义(对应于在Python中,无效的PHP格式)
  • 数字(Perl不区分整数和浮点)
  • 一根绳子
  • 对任何其他变量的引用。
my$undef=undef;打印$undef;#打印空字符串“”并引发警告#隐式undef:我的$undef2;打印$undef2;#打印“”并发出完全相同的警告
我的$num=4040.5;打印$num;#"4040.5"
my$string=“world”;打印$string;#“世界”

(参考文献很快就会出现。)

使用.运算符(与PHP相同):

打印“Hello”(你好)$字符串;#“你好,世界”

“布尔型”

Perl没有布尔数据类型。中的标量如果语句的计算结果为布尔值“false”,当且仅当它是以下值之一时:

  • 未定义
  • 0
  • 一串“”
  • 一串"0".

Perl文档反复地声称函数在某些情况下返回“true”或“false”值。实际上,当一个函数被声明返回“true”时,它通常会返回1,当声称返回false时,它通常返回空字符串,“”.

弱输入

无法确定标量是包含“数字”还是“字符串”。更准确地说,不应该这样做。标量的行为像数字还是字符串取决于使用它的运算符。当用作字符串时,标量的行为与字符串类似。当用作数字时,标数的行为与数字类似(如果不可能,则发出警告):

我的$str1=“4G”;my$str2=“4H”;打印$str1$字符串2;#“4G4H”打印$str1+$str2;#带有两个警告的“8”打印$str1 eq$str2;#“”(空字符串,即false)打印$str1==$str2;#带两个警告的“1”#经典错误打印“yes”==“no”;#“1”有两个警告;当用作数字时,这两个值的计算结果都为0

教训是在正确的情况下始终使用正确的运算符。有单独的运算符用于将标量作为数字进行比较,并将标量用作字符串进行比较:

#数值运算符:<,>,<=,>=,==,!=,<=>,+*#字符串运算符:lt、gt、le、ge、eq、ne、cmp,.,x个

数组变量

数组变量是由从0开始的整数索引的标量列表。在Python中,这被称为列表在PHP中,这称为阵列。使用带括号的标量列表声明数组:

我的@array=(“打印”,“这些”,“字符串”,“退出”,“for”,“me”,#尾随逗号可以);

您必须使用美元符号来访问数组中的值,因为该值恢复不是数组,而是标量:

打印$array[0];#“打印”打印$array[1];#“这些”打印$array[2];#“字符串”打印$array[3];#“退出”打印$array[4];#“用于”打印$array[5];#“我”打印$array[6];#返回undef,打印“”并引发警告

您可以使用负索引来检索从末尾开始并向后工作的条目:

打印$array[-1];#“我”打印$array[-2];#“用于”打印$array[-3];#“退出”打印$array[-4];#“字符串”打印$array[-5];#“这些”打印$array[-6];#“打印”打印$array[-7];#返回undef,打印“”并引发警告

标量之间没有冲突$变量和一个阵列@无功功率,无功功率包含标量条目$var[0]。然而,读者可能会感到困惑,所以要避免这种情况。

要获取数组的长度:

打印“此数组有”。(标量@数组)。“元素”;#“此数组有6个元素”打印“最后填充的索引是”$#数组;#“最后填充的索引为5”

调用原始Perl脚本时使用的参数存储在内置数组变量 @ARGV公司.

变量可以插入到字符串中:

打印“Hello$string”;#“你好,世界”打印“@array”;#“为我打印这些字符串”

小心。有一天你会把某人的电子邮件地址放在一个字符串中,"jeff@gmail.com"。这将导致Perl查找一个名为@gmail公司插入到字符串中,而没有找到它,从而导致运行时错误。插值可以通过两种方式来防止:通过反斜杠跳过符号,或者使用单引号代替双引号。

打印“Hello\$string”;#“你好$string”打印“Hello$string”;#“你好$string”打印“\@array”;#“@array”打印“@array”;#“@array”

哈希变量

散列变量是由字符串索引的标量列表。在Python中,这被称为词典在PHP中,它被称为阵列.

我的%科学家=(“牛顿”=>“艾萨克”,“爱因斯坦”=>“阿尔伯特”,“达尔文”=>“查尔斯”,);

请注意,此声明与数组声明非常相似。事实上,双箭头符号=>被称为“胖逗号”,因为它只是逗号分隔符的同义词。哈希是使用包含偶数个元素的列表声明的,其中偶数个的元素(0、2、…)都被视为字符串。

同样,您必须使用美元符号从散列中访问值,因为该值恢复不是散列,而是标量:

打印$scientists{“Newton”};#“艾萨克”打印$科学家{“爱因斯坦”};#“阿尔伯特”打印$科学家{“达尔文”};#“查尔斯”打印$scientists{“Dyson”};#返回undef,打印“”并引发警告

请注意此处使用的大括号。同样,标量之间没有冲突$变量和散列%无功功率,无功功率包含标量条目$var{“foo”}.

您可以将哈希直接转换为具有两倍多条目的数组,在键和值之间交替(反过来也很容易):

我的@scientists=%科学家;

然而,与数组不同,散列的键具有无基础订单。它们将以更有效的顺序返回。因此,请注意重新安排的秩序但保存完好在结果数组中:

打印“@scientists”;#比如“爱因斯坦-阿尔伯特-达尔文-查尔斯·牛顿-艾萨克”

重述一下,您必须使用方括号从数组中检索值,但必须使用支架从散列中检索值。方括号实际上是一个数值运算符,大括号实际上是字符串运算符。事实上指数提供的是一个数字或字符串,它完全没有意义:

my$data=“orange”;my@data=(“purple”);我的%数据=(“0”=>“蓝色”);打印$data;#“橙色”打印$data[0];#“紫色”打印$data[“0”];#“紫色”打印$data{0};#“蓝色”打印$data{“0”};#“蓝色”

列表

A类列表在Perl中,无论是数组还是散列都是不同的。您刚刚看到了几个列表:

(“打印”,“这些”,“字符串”,“退出”,“for”,“我”,)(“牛顿”=>“艾萨克”,“爱因斯坦”=>“阿尔伯特”,“达尔文”=>“查尔斯”,)

列表不是变量。列表是短暂的价值可以是分配数组或散列变量。这就是为什么声明数组和散列变量的语法是相同的。在许多情况下,术语“列表”和“数组”可以互换使用,但在同样多的情况下,列表和数组显示出细微不同且极为混乱的行为。

可以。记住=>只是,伪装,然后看这个例子:

(“一”,1,“三”,3,“五”,5)(“一”=>1,“三”=>3,“五”=>5)

使用=>提示其中一个列表是数组声明,另一个是散列声明。但就其本身而言,它们都不是什么声明。它们只是列表。完全相同列表。也:

()

这里甚至没有任何提示。此列表可用于声明空数组或空散列珍珠口译员显然无法说出这两种方式。一旦理解了Perl的这一奇怪方面,您也会理解为什么以下事实必须是正确的:列表值不能嵌套。试试看:

我的@array=(“苹果”,“香蕉”,(“内部”,“列表”,“多个”,“条目”,),“樱桃”,);

Perl无法知道(“内部”、“列表”、“多个”、“条目”)应该是内部数组或内部哈希。因此,Perl假定它既不是,也不是将列表扁平化为一个长列表:

打印$array[0];#“苹果”打印$array[1];#“香蕉”打印$array[2];#“内部”打印$array[3];#“列表”打印$array[4];#“几个”打印$array[5];#“条目”打印$array[6];#“樱桃”

无论是否使用胖逗号,情况都一样:

我的%hash=(“啤酒”=>“好”,“香蕉”=>(“绿色”=>“等待”,“yellow”=>“吃”,),);#上面给出了一个警告,因为哈希是使用7元素列表声明的打印$hash{“啤酒”};#“很好”打印$hash{“香蕉”};#“绿色”打印$hash{“wait”};#“黄色”;打印$hash{“吃”};#undef,因此打印“”并引发警告

当然,这确实使将多个数组连接在一起变得容易:

my@bones=(“肱骨”,(“颌骨”,“头骨”),“胫骨”);my@fingers=(“拇指”、“索引”、“中间”、“戒指”、“小”);my@parts=(@骨骼,@手指,(“脚”,“脚趾”),“眼球”,“关节”);打印@部件;

稍后将对此进行详细介绍。

上下文

Perl最显著的特点是它的代码是上下文敏感的.Perl中的每个表达式都在标量上下文或列表上下文中求值,取决于它应该生成标量还是列表。在不知道表达式求值的上下文的情况下,不可能确定它将求值到什么。

标量赋值,例如我的$标量=在标量上下文中计算其表达式。这里的表达式是“门捷列夫”:

my$scalar=“门捷列夫”;

数组或散列赋值,例如我的@数组=我的%散列=在列表上下文中计算其表达式。这里的表达式是(“阿尔法”、“贝塔”、“伽玛”、“馅饼”)(或(“Alpha”=>“Beta”,“Gamma”==>“Pie”),两者相当):

my@array=(“阿尔法”,“贝塔”,“伽玛”,“饼图”);my%hash=(“Alpha”=>“Beta”,“Gamma”=>“Pie”);

在列表上下文中计算的标量表达式会自动转换为单元素列表:

my@array=“门捷列夫”;#与'my@array=(“Mendeleev”);'相同

在标量上下文中计算的数组表达式返回数组的长度:

my@array=(“阿尔法”,“贝塔”,“伽玛”,“饼图”);my$scalar=@array;打印$scalar;#"4"

在标量上下文中计算的列表表达式(列表不同于数组,记得吗?)返回的不是列表的长度,而是列表中的最后一个标量:

my$scalar=(“Alpha”、“Beta”、“Gamma”和“Pie”);打印$scalar;#“馅饼”

这个打印内置函数在列表上下文中计算其所有参数。事实上,打印接受不受限制的参数列表并逐个打印,这意味着可以使用它直接打印数组:

my@array=(“Alpha”,“Beta”,“Goo”);my$scalar=“-X-”;打印@array;#“AlphaBetaGoo”;打印$scalar,@array,98;#“-X-AlphaBetaGoo98”;

小心。许多Perl表达式和内置函数根据所评估的上下文显示完全不同的行为。最突出的例子是函数颠倒在列表上下文中,颠倒将其参数视为列表,并反转该列表。在标量上下文中,颠倒将整个列表连接在一起,然后将其反转为单个单词。

反向打印“hello world”;#“你好,世界”my$string=reverse“hello world”;打印$string;#“德罗·奥利赫”

可以使用标量内置功能:

打印标量反转“hello world”;#“德罗·奥利赫”

记住我们是如何使用的标量早些时候,为了得到数组的长度?

引用和嵌套数据结构

与列表不能包含作为元素的列表一样,数组和哈希不能包含其他数组和哈什作为元素。它们只能包含标量。观察我们尝试时会发生什么:

my@outer=(“太阳”,“水星”,“金星”,undef,“火星”);my@inner=(“地球”、“月亮”);$outer[3]=@内部;打印$outer[3];#"2"

$外部[3]是标量,因此需要标量值。当您尝试分配数组值时,如@内部到它,@内部在标量上下文中计算。这与分配相同标量@内部,它是数组的长度@内部,即2。

但是,标量变量可能包含参考到任何变量,包括数组变量或散列变量。这就是如何在Perl中创建更复杂的数据结构。

使用反斜杠创建引用。

my$colour=“靛蓝”;我的$scalarRef=\$colour;

任何时候使用变量名时,都可以只放一些大括号,然后在大括号内放一个参考改为变量。

打印$colour;#“靛蓝”打印$scalarRef;#例如“标尺(0x182c180)”打印${$scalarRef};#“靛蓝”

只要结果不含糊,也可以省略大括号:

打印$$scalarRef;#“靛蓝”

如果引用是对数组或散列变量的引用,则可以使用大括号或更流行的箭头运算符从中获取数据,->:

my@colors=(“红色”、“橙色”、“黄色”、“绿色”、“蓝色”);我的$arrayRef=\@colors;打印$颜色[0];#直接阵列存取打印${$arrayRef}[0];#使用引用访问数组打印$arrayRef->[0];#完全一样的东西my%原子重量=(“氢”=>1.008,“氦”=>4.003,“锰”=>54.94);我的$hashRef=\%atomicWeights;打印$atomicWeights{“氦”};#直接散列访问打印${$hashRef}{“氦”};#使用引用获取散列打印$hashRef->{“氦”};#完全一样,这很常见

声明数据结构

这里有四个例子,但实际上最后一个是最有用的。

我的%owner1=(“name”=>“圣诞老人”,“DOB”=>“1882-12-25”,);my$owner1Ref=\%owner1;我的%owner2=(“name”=>“米老鼠”,“出生日期”=>“1928-11-18”,);我的$owner2Ref=\%owner2;my@owners=($owner1Ref,$owner2Ref);我的$ownersRef=\@owners;我的%帐户=(“数字”=>“12345678”,“打开”=>“2000-01-01”,“所有者”=>$ownersRef,);

这显然是不必要的,因为您可以将其缩短为:

我的%owner1=(“name”=>“圣诞老人”,“出生日期”=>“1882-12-25”,);我的%owner2=(“name”=>“米老鼠”,“出生日期”=>“1928-11-18”,);my@owners=(\%owner1,\%ower2);我的%帐户=(“数字”=>“12345678”,“打开”=>“2000-01-01”,“所有者”=>\@所有者,);

也可以申报匿名的使用不同符号的数组和散列。对匿名数组使用方括号,对匿名哈希使用大括号。每种情况下返回的值是参考到所讨论的匿名数据结构。仔细观察,结果完全相同%帐户如上所述:

#大括号表示匿名散列我的$owner1Ref={“name”=>“圣诞老人”,“出生日期”=>“1882-12-25”,};我的$owner2Ref={“name”=>“米老鼠”,“DOB”=>“1928-11-18”,};#方括号表示匿名数组我的$ownersRef=[$owner1Ref,$owner2Ref];我的%帐户=(“数字”=>“12345678”,“打开”=>“2000-01-01”,“所有者”=>$ownersRef,);

或者,简而言之(这是您应该填写的表格事实上在声明内联复杂数据结构时使用):

我的%帐户=(“数字”=>“31415926”,“打开”=>“3000-01-01”,“所有者”=>[{“name”=>“Philip Fry”,“DOB”=>“1974-08-06”,},{“name”=>“Hubert Farnsworth”,“出生日期”=>“2841-04-09”,},],);

从数据结构中获取信息

现在,让我们假设您仍然有%帐户到处乱踢,但所有其他事情(如果还有其他事情的话)都超出了范围。您可以通过在每种情况下反转相同的过程来打印信息。这里有四个例子,其中最后一个最有用:

我的$ownersRef=$account{“owners”};my@owners=@{$ownersRef};我的$owner1Ref=$owners[0];我的%owner1=%{$owner1Ref};my$owner2Ref=$owners[1];我的%owner2=%{$owner2Ref};打印“帐号”,$Account{“数字”},“\n”;打印“Opened on”,$account{“Opened”},“\n”;打印“联合所有者:\n”;打印“\t”,$owner1{“name”},“(出生”,$所有者1{”DOB“},”)\n“;打印“\t”,$owner2{“name”},“(出生”,$所有者2{”DOB“},”)\n“;

或者,简称:

my@owners=@{$account{“owners”}};我的%owner1=%{$所有者[0]};我的%owner2=%{$所有者[1];打印“账号”,$Account{“number”},“\n”;print“Opened on”,$account{“Opened”},“\n”;打印“联合所有者:\n”;打印“\t”,$owner1{“name”},“(出生”,$所有者1{”DOB“},”)\n“;打印“\t”,$owner2{“name”},“(出生”,$所有者2{”DOB“},”)\n“;

或者使用引用和->操作员:

我的$ownersRef=$account{“owners”};我的$owner1Ref=$ownersRef->[0];我的$owner2Ref=$ownersRef->[1];打印“帐号”,$Account{“数字”},“\n”;打印“Opened on”,$account{“Opened”},“\n”;打印“联合所有者:\n”;打印“\t”,$owner1Ref->{“name”},“(出生”,$所有者1Ref->{“DOB”}”)\n“;打印“\t”,$owner2Ref->{“name”},“(出生”,$wner2Ref->{“DOB”}”)\n“;

如果我们完全跳过所有中间值:

打印“帐号”,$Account{“数字”},“\n”;打印“Opened on”,$account{“Opened”},“\n”;打印“联合所有者:\n”;打印“\t”,$account{“所有者”}->[0]->{“名称”},“(出生”,$account{”owners“}->[0]->}”DOB“},”)\n“;打印“\t”,$account{“owners”}->[1]->{“name”},“(出生”,$account{”owners“}->[1]->{”DOB“},”)\n“;

如何使用数组引用射击自己的脚

此数组有五个元素:

我的@array1=(1,2,3,4,5);打印@array1;#"12345"

然而,此数组有一个元素(碰巧是对匿名五元素数组的引用):

我的@array2=[1,2,3,4,5];打印@array2;#例如“阵列(0x182c180)”

这个标量是对匿名五元素数组的引用:

我的$array3Ref=[1,2,3,4,5];打印$array3Ref;#例如“阵列(0x22710c0)”打印@{$array3Ref};#"12345"打印@$array3Ref;#"12345"

条件

如果...elsif公司...其他的...

除了拼写埃尔西夫:

my$word=“反歧视主义”;我的$strlen=长度$word;如果($strlen>=15){打印“”,$word,“”是一个很长的单词“;}elsif(10<=$strlen&&$strlen<15){print“”,$word,“”是一个中等长度的单词“;}其他{打印“”,$word,“”是一个短单词“;}

Perl提供了一个更短的“陈述 如果 条件“强烈建议使用的语法短的声明:

print“'”,$word,“'实际上是巨大的”,如果$strlen>=20;

除非...其他的...

我的$温度=20;除非($温度>30){打印$temperature,“摄氏度不是很热”;}其他{打印$temperature,“摄氏度实际上相当热”;}

除非一般来说,最好像瘟疫一样避免使用积木,因为它们很容易混淆。安“除非[...其他的]“可以将块平凡地重构为”如果[...其他的]“通过否定条件[或保留条件并交换块]来阻止。幸运的是,没有除非关键字。

相比之下,强烈建议这样做,因为它很容易阅读:

打印“噢,不,太冷了”,除非$温度>15;

三元运算符

三元运算符?:允许简单如果要嵌入到语句中的语句。其规范用法是单数/复数形式:

我的美元收益=48;打印“你获得了”,$gain,“”,($gain==1?“经验点”:“经验点“),“!”;

旁白:在这两种情况下,单数和复数的拼写都是最好的。不要做像下面这样聪明的事情,因为任何搜索代码库来替换单词“tooth”或“toots”的人都不会找到这一行:

我的损失美元=1;打印“You lost”,$lost,“t”,($lost==1?“oo”:“ee”),“th!”;

三元运算符可以嵌套:

我的$eggs=5个;打印“你有”,$eggs==0?“无蛋”:$鸡蛋==1?“鸡蛋”:“一些鸡蛋”;

如果语句在标量上下文中评估其条件。例如,if(@array)当且仅当@阵列包含1个或多个元素。这些元素是什么并不重要,它们可能包含未定义或其他我们关心的错误值。

循环

有不止一种方法可以做到。

Perl有一个传统的虽然循环:

我的$i=0;while($i<标量@数组){打印$i,“:”,$array[$i];$i++;}

Perl还提供了直到关键词:

我的$i=0;直到($i>=标量@数组){打印$i,“:”,$array[$i];$i++;}

这些循环是几乎等同于上述内容(如果@阵列为空):

我的$i=0;做{打印$i,“:”,$array[$i];$i++;}while($i<标量@数组);

我的$i=0;做{打印$i,“:”,$array[$i];$i++;}直到($i>=标量@数组);

基本C型对于循环也可用。请注意我们如何将我的在内部对于语句,声明1美元仅适用于循环范围:

for(my$i=0;$i<标量@数组;$i++){打印$i,“:”,$array[$i];}#$我已经不在这里了,这里要整洁得多。

这种类型的对于循环被认为是过时的,应尽可能避免使用。列表上的本机迭代要好得多。注意:与PHP不同对于foreach公司关键词是同义词。只需使用看起来最可读的内容:

foreach my$string(@array){打印$string;}

如果您确实需要索引范围运算符 ..创建匿名整数列表:

每个$i(0..$#数组){打印$i,“:”,$array[$i];}

你不能迭代散列。然而,您可以迭代其键。使用钥匙内置函数检索包含哈希的所有键的数组。然后使用foreach公司我们用于阵列的方法:

foreach my$key(keys%科学家){打印$key,“:”,$scientists{$key};}

由于散列没有基本顺序,因此可以按任何顺序返回键。使用分类内置函数,用于预先按字母顺序对键数组进行排序:

foreach my$key(排序键%科学家){打印$key,“:”,$scientists{$key};}

如果不提供显式迭代器,Perl将使用默认迭代器,$_.$_是第一个也是最友好的内置变量:

foreach(@数组){打印$_;}

如果使用默认迭代器,并且只希望在循环中放置一条语句,则可以使用超短循环语法:

打印$_foreach@array;

回路控制

下一个最后的可以用来控制循环的进度。在大多数编程语言中,这些被称为持续打破分别是。我们还可以为任何循环提供标签。按照惯例,标签是用所有资本.标记好回路后,下一个最后的可能针对该标签。这个例子发现素数低于100:

候选人:我的$候选人(2..100){对于我的$除数(2。。平方英尺$候选人){如果$CANDIDATE%$divisor==0,则为下一个候选日期;}打印$candidate。“是质数”;}

数组函数

就地阵列修改

我们将使用@堆栈为了证明这些:

my@stack=(“弗雷德”、“艾琳”、“丹尼斯”、“查理”);打印@stack;#“FredEileenDeniseCharlie”

流行音乐提取并返回数组的最后一个元素。这可以看作是堆栈的顶部:

打印pop@stack;#“查理”打印@stack;#“弗雷德·艾琳·德尼塞”

将额外元素附加到数组末尾:

push@stack,“Bob”,“Alice”;打印@stack;#“FredEileenDeniseObAlice”

转移提取并返回数组的第一个元素:

打印移位@stack;#“弗雷德”打印@stack;#“EileenDeniseBobAlice”

取消移位在数组的开头插入新元素:

unshift@stack,“Hank”,“Grace”;打印@stack;#“HankGraceEileenDeniseBobAlice”

流行音乐,,转移取消移位都是特殊情况吗剪接.剪接删除并返回一个数组切片,并将其替换为其他数组切片:

打印拼接(@stack,1,4,“<<<”,“>>>”);#“GraceEileenDeniseBob”打印@stack;#“汉克·爱丽丝”

从旧阵列创建新阵列

Perl提供了以下函数,这些函数作用于数组以创建其他数组。

这个参加函数将多个字符串连接为一个:

my@elements=(“锑”、“砷”、“铝”、“硒”);打印@elements;#“锑砷铝硒”打印“@elements”;#“锑砷铝硒”打印连接(“,”,@elements);#锑、砷、铝、硒

在列表上下文中颠倒函数以相反的顺序返回列表。在标量上下文中,颠倒将整个列表连接在一起,然后将其反转为单个单词。

反向打印(“Hello”,“World”);#“世界你好”反向打印(“HelloWorld”);#“HelloWorld”(你好世界)打印标量反转(“HelloWorld”);#“dlroWolleH”打印标量反转(“Hello”、“World”);#“dlroWolleH”

这个地图函数将数组作为输入,并对每个标量应用一个操作$_在此数组中。然后从结果中构造一个新数组。要执行的操作以大括号内的单个表达式的形式提供:

my@capitals=(“巴吞鲁日”、“印第安纳波利斯”、“哥伦布”、“蒙哥马利”、“海伦娜”、“丹佛”、“博伊西”);打印连接“,”,映射{uc$}@capitals;#“巴顿路,印第安纳波利斯,哥伦布,蒙哥马利,海伦娜,丹佛,博伊斯”

这个格雷普函数接受一个数组作为输入,并返回一个过滤后的数组作为输出。语法类似于地图。这一次,为每个标量计算第二个参数$_在输入数组中。如果返回布尔值true,标量将被放入输出数组,否则不会。

打印连接“,”,grep{length$_==6}@capitals;#“丹佛海伦娜”

显然,结果数组的长度是成功匹配的次数,这意味着您可以使用格雷普要快速检查数组是否包含元素,请执行以下操作:

打印标量grep{$_eq“Columbus”}@capitals;#"1"

格雷普地图可以组合成列表推导式,这是一个非常强大的功能,在许多其他编程语言中都明显没有。

默认情况下分类函数返回按词法(字母)顺序排序的输入数组:

my@elevations=(19、1、2、100、3、98、100、1056);打印连接“,”,排序@elevations;# "1, 100, 100, 1056, 19, 2, 3, 98"

然而,与格雷普地图,您可以提供自己的一些代码。排序总是使用两个元素之间的一系列比较来执行。你的区块收到美元十亿美元作为输入,如果美元是“小于”十亿美元,如果“相等”则为0,如果美元“大于”十亿美元.

这个化学机械抛光运算符对字符串执行此操作:

打印连接“,”,排序{$acmp$b}@elevations;# "1, 100, 100, 1056, 19, 2, 3, 98"

“宇宙飞船操作员”,<=>,对数字执行相同操作:

打印连接“,”,排序{$a<=>$b}@elevations;# "1, 2, 3, 19, 98, 100, 100, 1056"

美元十亿美元始终是标量,但它们可以引用非常复杂的对象,很难进行比较。如果需要更多空间进行比较,可以创建单独的子例程并提供其名称:

次级比较器{#大量代码。。。#返回-1、0或1}打印连接“,”,排序比较器@elevations;

你不能这样做格雷普地图操作。

请注意,子程序和块从未显式提供美元十亿美元.喜欢$_,美元十亿美元实际上是全局变量已填充每次都要比较一对值。

内置功能

到目前为止,您已经看到了至少十几个内置函数:打印,分类,地图,格雷普,钥匙,标量内置函数是Perl最大的优势之一。他们

  • 数量众多
  • 非常有用
  • 广泛记录
  • 语法差异很大,请查看文档
  • 有时接受正则表达式作为参数
  • 有时接受整个代码块作为参数
  • 有时参数之间不需要逗号
  • 有时会使用任意数量的逗号分隔参数,有时则不会
  • 如果提供的参数太少,有时会填写自己的参数
  • 除非在模糊的情况下,否则通常不需要在论点周围加括号

关于内置函数的最佳建议是知道它们的存在浏览文档以备将来参考。如果你正在执行一项任务,感觉它很低级,而且很常见,以至于它以前已经做过多次了,那么它很可能已经完成了。

用户定义的子例程

使用附属的关键字。与内置函数相比,用户定义的子程序总是接受相同的输入:标量列表。当然,这个列表可能只有一个元素,也可能是空的。单个标量被视为具有单个元素的列表。带有的散列N个元素被视为带有2的列表N个元素。

尽管括号是可选的,但子程序应始终使用括号调用,即使调用时没有参数。这清楚地表明正在进行子例程调用。

进入子例程后,可以使用内置数组变量 @_。示例:

分连字符{#从数组中提取第一个参数,忽略其他所有参数my$word=shift@_;#过于聪明的列表理解$word=加入“-”,映射{子(substr)$word,$_,1}(0(长度$word)-1);返回$word;}打印连字符(“消灭”);#“e-x-t-e-r-m-i-n-a-t-e”

通过引用进行Perl调用

与几乎所有其他主要编程语言不同,Perl通过引用进行调用。这意味着子程序体中可用的变量或值不是原始的副本。他们原件。

我的$x=7;再分配{$_[0]=42;}重新分配($x);打印$x;#"42"

如果你尝试一下

重新分配(8);

然后发生错误并停止执行,因为重新分配()等于

8 = 42;

这显然是胡说八道。

要吸取的教训是,在子程序的主体中,在使用参数之前,应该始终先将其解压缩。

正在解包参数

打开包装有多种方法@_,但有些人比其他人优越。

示例子例程左侧_页面下面使用提供的填充字符将字符串填充到所需的长度。x个函数将同一字符串的多个副本连接在一行中。)(注意:为了简洁起见,这些子程序都缺少一些基本的错误检查,即确保填充字符只有1个字符,检查宽度是否大于或等于现有字符串的长度,检查是否传递了所有需要的参数。)

左侧_页面通常按如下方式调用:

打印left_pad(“hello”,10,“+”);#“+++++你好”
  1. 打开包装@_逐条输入很有效,但不太好看:

    sub-left_pad(副左侧_页){我的$oldString=$_[0];我的$width=$_[1];我的$padChar=$_[2];my$newString=($padChar x($width-length$oldString))$oldString;return$newString;}
  2. 打开包装@_通过使用从中删除数据转移建议最多使用4个参数:

    子左_pad{my$oldString=移位@_;my$width=shift@_;my$padChar=shift@_;my$newString=($padChar x($width-length$oldString))$oldString;旧字符串;return$newString;}

    如果没有数组提供给转移功能,然后运行@_隐式地。这种方法很常见:

    sub-left_pad(副左侧_页){my$oldString=移位;my$width=移位;my$padChar=移位;my$newString=($padChar x($width-length$oldString))$oldString;return$newString;}

    除了4个参数外,很难跟踪分配到哪里的内容。

  3. 你可以打开行李@_使用多个同时标量赋值的一键通。同样,这对于最多4个参数来说是可以的:

    sub-left_pad(副左侧_页){my($oldString,$width,$padChar)=@_;my$newString=($padChar x($width-length$oldString))$oldString;return$newString;}
  4. 对于具有大量参数的子程序,或者某些参数是可选的或不能与其他参数组合使用的子程序来说,最佳实践是要求用户在调用子程序时提供参数散列,然后解压缩@_回到那个散乱的参数中。对于这种方法,我们的子例程调用看起来有点不同:

    打印left_pad(“oldString”=>“pod”,“width”=>10,“padChar”=>”+“);

    子程序本身如下所示:

    sub-left_pad(副左侧_页){我的%args=@_;my$newString=($args{“padChar”}x($args{“width”}-length$args}“oldString”}))$args{“oldString”};return$newString;返回$newString;}

返回值

与其他Perl表达式一样,子例程调用可能会显示上下文行为。您可以使用万塔雷函数(应该调用野心家但没关系)检测子例程在哪个上下文中进行求值,并返回适合该上下文的结果:

子上下文子例程{#呼叫者需要列表。返回列表如果wantarray,返回(“珠穆朗玛峰”、“K2”、“Etna”);#调用方需要标量。返回标量返回3;}my@array=contextualSubroutine();打印@array;#“珠穆朗玛峰K2Etna”my$scalar=contextualSubroutine();打印$scalar;#"3"

系统调用

如果您已经知道以下与Perl无关的事实,请致歉。每次在Windows或Linux系统上(我猜想,在大多数其他系统上)完成一个进程时,它都会以16位结束状态字.最高8位构成返回代码介于0和255之间,0通常表示不合格的成功,其他值表示不同程度的失败。其他8位的检查频率较低,它们“反映了故障模式,如信号死亡和堆芯转储信息”。

您可以使用选择的返回代码(从0到255)退出Perl脚本出口.

Perl提供了多种方法-在单个调用中-生成子进程,暂停当前脚本直到子进程完成,然后恢复对当前脚本的解释。无论使用哪种方法,您都会立即发现内置标量变量 $?已使用该子进程终止时返回的状态字填充。只需取16位中最高的8位即可获得返回码:$? >> 8.

这个系统函数可用于使用列出的参数调用另一个程序。返回的值系统美元?已填充:

my$rc=system“perl”,“anotherscript.pl”,”foo“,”bar“,”baz“;$rc>>=8;打印$rc;#"37"

或者,您可以使用反勾号``在命令行中运行实际命令并捕获该命令的标准输出。在标量上下文中,整个输出作为单个字符串返回。在列表上下文中,所有输出作为字符串数组返回,每个字符串代表一行输出。

my$text=`perl anotherscript.pl foo bar baz`;打印$text;#“foobarbaz”

这是一种行为,如果另一个脚本.pl包含,例如:

使用严格;使用警告;打印@ARGV;37号出口;

文件和文件句柄

标量变量可以包含文件句柄而不是数字/string/reference或未定义。文件句柄本质上是对特定文件中特定位置的引用。

使用打开将标量变量转换为文件句柄。打开必须提供模式.模式<表示我们希望打开文件以从中读取:

my$f=“text.txt”;my$result=打开我的$fh,“<”,$f;if(!$result){死亡“无法打开”$f.“”用于阅读,因为:“.$!;}

如果成功,打开返回一个真值。否则,它将返回false,并在内置变量中填充错误消息$!如上所示,您应该始终检查打开操作已成功完成。这种检查相当乏味,一个常见的习惯用法是:

open(my$fh,“<”,$f)|| die“Couldn not open”(无法打开)$f.“”用于阅读,因为:“.$!;

注意需要在打开调用的参数。

要从文件句柄读取一行文本,请使用读行内置函数。读行返回一整行文本,并在其末尾保留换行符(可能文件的最后一行除外),或未定义如果你已经到了文件的末尾。

而(1){my$line=readline$fh;最后一个,除非定义$line;#处理生产线。。。}

要截断可能的尾部换行符,请使用咀嚼:

chomp$line;

请注意咀嚼作用于$行到位。$line=chomp$line可能不是你想要的。

您还可以使用电动势要检测是否已到达文件末尾,请执行以下操作:

而(!eof$fh){my$line=readline$fh;#处理$line。。。}

这也适用于:

while(我的$line=readline$fh){#处理$line。。。}

你会认为如果$行原来是单字符字符串"0",这是假的。然而,在这个特定的例子中,Perl神奇地将第一行代码while(已定义(my$line=readline$fh)){然而,这确实会打破:

我的$x=7;while($x==7和my$line=readline$fh){打印“行”,转储程序($line);}

Perl还提供了<>运算符,它是的同义词读行:

while(我的$line=<$fh>){#处理$line。。。}

甚至:

同时(<$fh>){#处理$_。。。}

写入文件首先需要以不同的模式打开它。模式>表示我们希望打开文件以写入(>如果目标文件已经存在并且包含内容,则将清除目标文件的内容。要仅附加到现有文件,请使用mode>>.)然后,只需为打印功能。

open(my$fh2,“>”,$f)|| die“无法打开”$f.“”用于写作,因为:“.$!;打印$fh2“老鹰已经离开巢穴”;

请注意,中间没有逗号2美元以及下一个论点。

当文件句柄超出范围时,它们实际上会自动关闭,否则:

关闭2美元;关闭$fh;

三个文件句柄作为全局常量存在:标准DIN,STDOUT公司STDERR公司。这些在脚本启动时自动打开。要读取单行用户输入:

我的$line=<STDIN>;

要等待用户按Enter键:

<标准DIN>;

打电话<>没有文件句柄时从中读取数据标准DIN,或者在调用Perl脚本时从参数中命名的任何文件中获取。

正如你可能已经收集到的,打印打印到STDOUT公司默认情况下,如果没有命名文件句柄。

文件测试

功能-e(电子)是一个内置函数,用于测试命名文件是否存在。

打印“what”,除非-e“/usr/bin/perl”;

功能-d日是一个内置函数,用于测试命名文件是否为目录。

功能-(f)是一个内置函数,用于测试命名文件是否为普通文件。

这只是其中的三个一大类函数表单的-X(X)哪里X(X)是一些小写或大写字母。这些函数称为文件测试。注意前面的减号。在谷歌查询中,减号表示排除包含此搜索词的结果。这使得谷歌很难进行文件测试!只需搜索“perl文件测试”即可。

正则表达式

正则表达式出现在除Perl之外的许多语言和工具中。Perl的核心正则表达式语法与其他地方基本相同,但Perl的满的正则表达式功能非常复杂,难以理解。我能给你的最好建议是尽可能避免这种复杂性。

匹配操作使用=~米//。在标量上下文中,=~米//成功时返回true,失败时返回false。

my$string=“Hello world”;if($string=~m/(\w+)\s+(\w+)/){打印“成功”;}

括号执行子匹配。成功执行匹配操作后,子匹配将填充到内置变量中$1,$2,$3, ...:

打印$1;#“你好”打印$2;#“世界”

在列表上下文中,=~米//收益$1,$2, ... 作为列表。

我的$string=“无色的绿色想法疯狂入睡”;my@matchs=$string=~m/(\w+)\s+((\w+)\s+(\w+))\s+(\w+)\s+(\w+)/;打印连接“,”,映射{“”.$_.“”}@matches;#打印“无色”、“绿色理念”、“环保”、“创意”、“睡眠”、“疯狂”

替换操作使用=~秒///.

my$string=“早上好,世界”;$string=~s/world/越南/;打印$string;#“早上好,越南”

请注意$string($string)已更改。您必须在=%s///操作。如果你传递一个文本字符串,你会得到一个错误。

这个/克标志表示“全局匹配”。

在标量上下文中,每个=~m//gcall在前一个匹配项之后查找另一个匹配,成功时返回true,失败时返回false。您可以访问$1然后以通常的方式继续。例如:

my$string=“一吨羽毛或一吨砖块”;while($string=~m/(\w+)/g){打印“”$1.“\n”;}

在列表上下文中=~m//g调用一次返回所有匹配项。

my@matches=$string=~m/(\w+)/g;打印连接“,”,映射{“”.$_.“”}@matches;

=~s///gcall执行全局搜索/替换并返回匹配的数量。在这里,我们用字母“r”替换所有元音。

#在没有/g的情况下尝试一次。$string=~s/[aeiou]/r/;打印$string;#“一吨羽毛或一吨砖块”#再一次。$string=~s/[aeiou]/r/;打印$string;#“一堆羽毛或一吨砖块”#然后使用/g完成其余操作$string=~s/[aeiou]/r/g;打印$string,“\n”;#“r trnnr rf frrthrrs rr r trnnr rf brrcks”

这个/我标志使匹配和替换区分大小写。

这个/x个flag允许正则表达式包含空白(例如换行符)和注释。

“你好,世界”=~m/(\w+)#一个或多个单词字符[]#单个文字空间,存储在字符类中world#literal“world”/x;#返回true

模块和包

在Perl中,模块和包是不同的。

模块

A类模块是一个下午点您可以使用的文件包括在另一个Perl文件(脚本或模块)中。模块是一个文本文件,其语法与.pl文件Perl脚本。示例模块可能位于C: \foo\bar\baz\Demo\StringUtils.pm/foo/bar/baz/Demo/StringUtils.pm文件,内容如下:

使用严格;使用警告;亚僵尸化{my$word=shift@_;$word=~s/[aeiou]/r/g;返回$word;}返回1;

因为模块在加载时是从上到下执行的,所以需要在末尾返回一个真值,以表明它已成功加载。

为了让Perl解释器能够找到它们,应该在环境变量中列出包含Perl模块的目录第15页打电话之前珍珠。列出包含模块的根目录,不要列出模块目录或模块本身:

设置PERL5LIB=“C:\foo\bar\baz;%PERL5LINB%”

export PERL5LIB=“/foo/bar/baz:$PERL5LIB”

创建Perl模块后珍珠知道在哪里查找它,您可以使用要求在Perl脚本中搜索和执行它的内置函数。例如,呼叫需要演示::StringUtils使Perl解释器搜索中列出的每个目录PERL5LIB公司反过来,查找名为演示/StringUtils.pm执行模块后,在那里定义的子例程突然可用于主脚本。我们的示例脚本可能被调用main.pl(主pl)内容如下:

使用严格;使用警告;需要演示::StringUtils;打印zombify(“我想要大脑”);#“r wrnt brrrns”

注意双冒号的用法::作为目录分隔符。

现在出现了一个问题:如果main.pl(主pl)包含许多要求调用,这样加载的每个模块都包含更多要求调用,则可能很难跟踪zombify()子程序。这个问题的解决方案是使用包。

包装

A类包裹可以在其中声明子例程的命名空间。您声明的任何子例程都在当前包中隐式声明。在执行开始时,您处于主要的包,但可以使用包装内置功能:

使用严格;使用警告;子子程序{打印“宇宙”;}包装食品:土豆;#无碰撞:子子程序{印刷“金德沃德”;}

注意双冒号的用法::作为命名空间分隔符。

每次调用子例程时,都会隐式调用当前包中的子例程。或者,您可以显式地提供一个包。看看如果我们继续上面的脚本会发生什么:

子例程();#“金德华”main::子例程();#“宇宙”食物::土豆::subroutine();#“金德华”

因此,上述问题的逻辑解决方案是修改C: \foo\bar\baz\Demo\StringUtils.pm/foo/bar/baz/Demo/StringUtils.pm文件阅读:

使用严格;使用警告;软件包演示::StringUtils;亚僵尸化{my$word=shift@_;$word=~s/[aeiou]/r/g;返回$word;}返回1;

并修改main.pl(主pl)阅读:

使用严格;使用警告;需要演示::StringUtils;打印演示::StringUtils::zombify(“我想要大脑”);#“r wrnt brrrns”

现在仔细阅读下面的内容。

包和模块是Perl编程语言的两个完全独立的特性。它们都使用相同的双冒号分隔符,这是一个巨大的转移注意力的问题。在脚本或模块的过程中,可以多次切换包,也可以在多个文件的多个位置使用相同的包声明。打电话需要Foo::Bar 不会查找并加载带有包装Foo::Bar声明,也不一定要在Foo::酒吧命名空间。打电话需要Foo::Bar只加载一个名为食品/酒吧.pm,无需任何它内部的包声明类型,实际上可能声明包Baz::Qux据你所知,这里面还有其他的胡说八道。

同样,子例程调用Baz::Qux::processThis()不一定在名为巴兹/库克.pm它本可以被宣布随便什么地方.

将这两个概念分开是Perl最愚蠢的特性之一,将它们作为单独的概念处理总是会导致混乱、令人恼火的代码。幸运的是,大多数Perl程序员都遵守以下两条定律:

  1. Perl脚本(.pl文件文件)必须始终包含零包裹声明。
  2. Perl模块(下午点文件)必须始终包含一个包裹声明,与它的名称和位置完全对应。例如模块演示/StringUtils.pm必须以开头包演示::StringUtils.

因此,在实践中,您会发现大多数“包”和“模块”都是由可靠的第三方生产的可以可以互换地看待和提及。然而,重要的是你不要认为这是理所当然的,因为有一天你满足一个疯子产生的代码。

面向对象的Perl

Perl不是一种很好的面向对象编程语言。Perl的OO功能是在事实发生后移植的,这一点可以看出。

  • 对象只是一个引用(即标量变量),它碰巧知道它的referent属于哪个类。要告诉引用它的reforent属于某个类,请使用祝福。要找出引用的referent属于哪个类(如果有的话),请使用裁判.

  • A类方法是一个简单的子例程,它需要一个对象(如果是类方法,则需要一个包名)作为其第一个参数。对象方法使用调用$obj->方法(); 类方法是使用调用的包::名称->方法().

  • A类只是一个碰巧包含方法的包。

一个简单的例子让这一点更加清楚。示例模块动画.pm包含类动物内容如下:

使用严格;使用警告;包装动物;分餐{#第一个参数始终是采取行动的对象。我的$self=轮班@_;给我的$食物(@_){if($self->can_eat($food)){打印“Eating”,$food;}其他{打印“不能吃”,$food;}}}#为了便于争论,假设动物可以吃任何东西。子caneat(_E){返回1;}返回1;

我们可以这样使用这个类:

需要动物;我的$动物={“腿”=>4,“color”=>“棕色”,};#$animal是一个普通的散列引用打印参考$animal;#“哈什”祝福$animal,“animal”;#现在它是“动物”类的对象打印参考$animal;#“动物”

注意:从字面上看,任何引用都可以被祝福进入任何类。您需要确保(1)referent实际上可以用作该类的实例,以及(2)所讨论的类存在并已被加载。

您仍然可以按常规方式使用原始散列:

打印“Animal has”,$Animal->{“legs”},“leg(s)”;

但您现在也可以使用相同的->操作员,就像这样:

$动物->吃(“昆虫”、“咖喱”、“桉树”);

最后一次调用相当于动物::吃($动物,“昆虫”,“咖喱”,“桉树”).

建造师

构造函数是返回新对象的类方法。如果你想要一个,就申报一个。你可以用任何你喜欢的名字。对于类方法,传递的第一个参数不是对象,而是类名。在这种情况下,“动物”:

使用严格;使用警告;包装动物;sub新{my$class=shift@_;返回祝福{“legs”=>4,“colour”=>“brown”},$class;}# ...等。

然后按如下方式使用:

my$animal=动物->new();

继承

要创建从父类继承的类,请使用使用父级。假设我们子类化了动物具有考拉,位于科拉帕.pm:

使用严格;使用警告;包装考拉;#从动物身上继承使用父母(“动物”);#覆盖一个方法子caneat(_E){my$self=shift@_;#未使用。你可以在这里输入“shift@_;”我的$food=shift@;return$food eq“桉树”;}返回1;

以及一些示例代码:

使用严格;使用警告;需要考拉;my$koala=koala->new();$考拉->吃(“昆虫”、“咖喱”、“桉树”);#只吃桉树

最后一个方法调用尝试调用考拉::吃($考拉,“昆虫”,“咖喱”,“桉树”),但它是一个子例程eat()未在中定义考拉包裹。然而,因为考拉有父类动物,Perl解释器尝试调用动物::吃($考拉,“昆虫”,“咖喱”,“桉树”)相反,这是有效的。注意类是如何动物由自动加载科拉帕.pm.

使用父级Perl接受父类名称列表,支持多重继承,具有所有的优点和缺点。

开始阻碍

A类开始块一旦执行珍珠已完成对该块的解析,甚至在它解析文件的其余部分之前。它在执行时被忽略:

使用严格;使用警告;打印“这是第二次打印”;开始{print“首先打印此内容”;}打印“这第三次打印”;

A类开始块总是先执行。如果创建多个开始块(没有),当编译器遇到它们时,它们将按从上到下的顺序执行。A类开始块总是首先执行,即使它被放置在脚本的中间(不要这样做)或末尾(或这样)。不要弄乱代码的自然顺序。放置开始一开始就有几个街区!

A类开始块被解析后立即执行。一旦完成,解析结束时的简历开始块。只有在解析了整个脚本或模块之后开始执行的块。

严格使用;使用警告;print“此‘print’语句解析成功但从未执行”;开始{打印“This gets printed first”;}打印“这也是成功解析但从未执行的”;……因为e4h8v3oitv8h4o8gch3o84c3下面有一个巨大的解析错误。

因为它们是在编译时执行的开始放置在条件块内的块将仍然首先执行,即使条件的计算结果为false,并且尽管条件尚未评估事实上可能永远不会被评估.

如果(0){开始{打印“这一定会被打印出来”;}打印“即使这不会”;}
不要把开始条件性块!如果要在编译时有条件地执行某些操作,则需要将条件里面这个开始块:

开始{if($条件){#等。}}

使用

可以。现在您已经了解了包、模块、类方法和开始方块,我可以解释非常常见的使用功能。

以下三项声明:

使用Caterpillar(“爬行”、“化蛹”);使用Caterpillar();使用Caterpillar;

分别相当于:

开始{需要Caterpillar;Caterpillar->导入(“爬行”、“化蛹”);}开始{需要Caterpillar;}开始{需要Caterpillar;Caterpillar->导入();}
  • 不,这三个例子的顺序没有错。只是Perl很蠢。
  • A类使用呼叫是伪装的开始块。同样的警告也适用。使用语句必须始终放在文件的顶部,并且从不包含条件句.
  • 导入()不是内置的Perl函数。它是一个用户定义的类方法。责任在于毛虫要定义或继承的包导入()理论上,该方法可以接受任何东西作为参数,并对这些参数执行任何操作。使用Caterpillar;可以做任何事。请参阅以下文档毛虫.pm来找出到底会发生什么。
  • 注意如何需要Caterpillar加载a模块命名毛虫.pm,而Caterpillar->导入()调用导入()毛虫 包装。让我们希望模块和包一致!

出口商

定义导入()方法是从出口商模块。Exporter是一个核心模块事实上的Perl编程语言的核心特性。出口商实施导入(),传入的参数列表被解释为子例程名称列表。当子程序导入()ed,它既可以在当前包中使用,也可以在自己的原始包中使用。

通过示例,这个概念最容易掌握。这是什么毛虫.pm看起来像:

使用严格;使用警告;卡特彼勒成套设备;#从导出器继承使用父项(“导出器”);sub-craw{打印“英寸”;}sub-eat{打印“chomp chomp”;}亚蛹{打印“bloop bloop”;}我们的@EXPORT_OK=(“爬行”,“吃”);返回1;

包变量@导出_确定应包含子例程名称列表。

然后,另一段代码可能导入()这些子例程的名称,通常使用使用声明:

使用严格;使用警告;使用Caterpillar(“爬行”);爬行();#“英寸-英寸”

在这种情况下,当前程序包是主要的,所以爬行()呼叫实际上是呼叫main::爬行(),(因为它是导入的)映射到Caterpillar::爬行().

注:无论@导出_确定,每种方法都可以称为“长手法”:

使用严格;使用警告;使用Caterpillar();#未命名子例程,未进行import()调用#然而。。。卡特彼勒::爬行();#“英寸-英寸”卡特彼勒::eat();#“狼吞虎咽”卡特彼勒::pulate();#“bloop bloop”

Perl没有私有方法。通常,用于私有用途的方法使用前导下划线或两个下划线命名。

@出口

Exporter模块还定义了一个名为@出口,也可以用子例程名称列表填充。

使用严格;使用警告;卡特彼勒成套设备;#从导出器继承使用父项(“导出器”);sub-craw{打印“英寸”;}sub-eat{打印“chomp chomp”;}化蛹{打印“bloop bloop”;}我们的@EXPORT=(“爬行”、“吃”、“化蛹”);返回1;

中命名的子例程@出口如果导入()调用时根本没有参数,这就是这里发生的情况:

使用严格;使用警告;使用Caterpillar;#调用不带参数的import()爬行();#“英寸-英寸”eat();#“狼吞虎咽”pupate();#“bloop bloop”

但请注意,我们是如何回到这样一种情况的,在没有其他线索的情况下,可能很难判断出在哪里爬行()最初定义为。这个故事的寓意是双重的:

  1. 创建使用Exporter的模块时,切勿使用@出口默认情况下导出子例程。始终让用户调用子程序“手动”或导入()它们显式地(使用例如。使用Caterpillar(“爬行”),这是一个很好的线索毛虫.pm用于定义爬行()).

  2. 什么时候?使用在使用Exporter的模块中,始终显式地命名要执行的子例程导入().如果你不想导入()任何子程序,如果希望直接引用它们,必须提供一个明确的空列表:使用Caterpillar().

随笔杂记

  • 核心模块数据::自卸车可用于将任意标量输出到屏幕。这是一个基本的调试工具。

  • 有一种替代语法,qw{},用于声明数组。这经常出现在使用声明:

    使用帐户qw{create-open-close-spend-delete};

    许多其他类似报价的运营商.

  • =~米//=~秒///操作时,可以使用大括号而不是斜杠作为正则表达式分隔符。如果正则表达式包含大量斜杠,这将非常有用,否则需要使用反斜杠进行转义。例如,=~m{//}匹配三个正斜杠,并且=~s{^https?://}{}删除URL的协议部分。

  • Perl确实有常量现在,这些都不受欢迎,但并非总是如此。常量实际上只是带有省略括号的子例程调用。

  • 有时人们会省略散列键周围的引号$hash{key}而不是$hash{“密钥”}他们可以逃脱惩罚,因为在这种情况下钥匙作为字符串出现“密钥”,与子例程调用相反键().

  • 如果您看到一块未格式化的代码用双V形分隔符包装,如<<EOF,对于谷歌来说,“here-doc”是一个神奇的词。

  • 警告!许多内置函数可以在不带参数的情况下调用,让他们动手术$_相反希望这将有助于您了解以下阵型:

    打印foreach@array;

    foreach(@array){下一个,除非另有规定;}

    我不喜欢这种形式,因为它会在重构时导致问题。

这是两个半小时。